package com.jrx.anytxn.transaction.handler.impl;

import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.jrx.anytxn.common.constant.Constants;
import com.jrx.anytxn.common.data.TxnRespCode;
import com.jrx.anytxn.common.exception.TxnBizException;
import com.jrx.anytxn.common.exception.TxnException;
import com.jrx.anytxn.common.uitl.BeanMapping;
import com.jrx.anytxn.common.uitl.CollectorsUtils;
import com.jrx.anytxn.common.uitl.DateUtils;
import com.jrx.anytxn.common.uitl.JsonUtils;
import com.jrx.anytxn.common.uitl.SeqUtils;
import com.jrx.anytxn.common.uitl.StringUtils;
import com.jrx.anytxn.customer.constant.BankCardConstant;
import com.jrx.anytxn.customer.constant.CustomerConstant;
import com.jrx.anytxn.customer.dto.customer.CustomerRes;
import com.jrx.anytxn.customer.entity.CmCustomerBankcardInfo;
import com.jrx.anytxn.customer.entity.CmCustomerExtInfo;
import com.jrx.anytxn.customer.entity.CmCustomerFirstLevel;
import com.jrx.anytxn.customer.entity.CmCustomerSecondLevel;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerBankcardInfoMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerExtInfoMapper;
import com.jrx.anytxn.customer.service.IBankCardService;
import com.jrx.anytxn.customer.service.ICustomerService;
import com.jrx.anytxn.feign.api.request.LoanPayOffReq;
import com.jrx.anytxn.feign.api.service.IUserApiService;
import com.jrx.anytxn.i18n.response.TxnRespResult;
import com.jrx.anytxn.number.exception.SegmentException;
import com.jrx.anytxn.number.service.ISegmentService;
import com.jrx.anytxn.param.constant.Constant;
import com.jrx.anytxn.param.constant.ErrorMsgConstant;
import com.jrx.anytxn.param.dto.aviator.RuleMatchReq;
import com.jrx.anytxn.param.dto.product.PrPrepaymentFeeRes;
import com.jrx.anytxn.param.dto.product.PrPreterminationFeeTableRes;
import com.jrx.anytxn.param.dto.system.SystemParameterRes;
import com.jrx.anytxn.param.entity.PmMerchantTable;
import com.jrx.anytxn.param.entity.PrCodeTable;
import com.jrx.anytxn.param.entity.PrInterestTable;
import com.jrx.anytxn.param.entity.PrOrganizationTable;
import com.jrx.anytxn.param.entity.PrProductTable;
import com.jrx.anytxn.param.enums.BizTypeEnum;
import com.jrx.anytxn.param.mapper.ext.ExtBtBatchStatusMapper;
import com.jrx.anytxn.param.mapper.ext.ExtPrCodeTableMapper;
import com.jrx.anytxn.param.mapper.ext.ExtPrOrganizationTableMapper;
import com.jrx.anytxn.param.service.aviator.IAviatorService;
import com.jrx.anytxn.param.service.partner.IPmMerchantTableService;
import com.jrx.anytxn.param.service.product.IInterestTableService;
import com.jrx.anytxn.param.service.product.IPrPrepaymentFeeService;
import com.jrx.anytxn.param.service.product.IPrPreterminationFeeTableService;
import com.jrx.anytxn.param.service.product.IProductTableService;
import com.jrx.anytxn.param.service.system.IOrganizationTableService;
import com.jrx.anytxn.param.service.system.ISystemParameterService;
import com.jrx.anytxn.param.utils.BizTypeProducts;
import com.jrx.anytxn.redisson.service.RedissonLockService;
import com.jrx.anytxn.redisson.util.LockType;
import com.jrx.anytxn.ribbon.config.GrayVersionConfig;
import com.jrx.anytxn.transaction.bean.BillResult;
import com.jrx.anytxn.transaction.bean.CurrentStatementBean;
import com.jrx.anytxn.transaction.bean.DebtBean;
import com.jrx.anytxn.transaction.bean.LoanPlanParamBean;
import com.jrx.anytxn.transaction.bean.PaymentLoanInfoBean;
import com.jrx.anytxn.transaction.bean.RepaymentParamBean;
import com.jrx.anytxn.transaction.constant.TransBizConstant;
import com.jrx.anytxn.transaction.constant.TransErrMsgConstant;
import com.jrx.anytxn.transaction.dto.common.CommonReq;
import com.jrx.anytxn.transaction.dto.loan.LoanConfirmReq;
import com.jrx.anytxn.transaction.dto.loan.LoanConfirmRes;
import com.jrx.anytxn.transaction.dto.loan.LoanInformationRes;
import com.jrx.anytxn.transaction.dto.loan.LoanRecordRes;
import com.jrx.anytxn.transaction.dto.loan.LoanReq;
import com.jrx.anytxn.transaction.dto.payment.AdvanceSettleTrialRes;
import com.jrx.anytxn.transaction.dto.payment.BillPayReq;
import com.jrx.anytxn.transaction.dto.payment.BillPayTrailReq;
import com.jrx.anytxn.transaction.dto.payment.BillPayTrailRes;
import com.jrx.anytxn.transaction.dto.payment.BillPrepayNextReq;
import com.jrx.anytxn.transaction.dto.payment.BillPrepayNextRes;
import com.jrx.anytxn.transaction.dto.payment.CustPayOffQueryRes;
import com.jrx.anytxn.transaction.dto.payment.CustRepaymentQueryReq;
import com.jrx.anytxn.transaction.dto.payment.CustRepaymentQueryRes;
import com.jrx.anytxn.transaction.dto.payment.CustomerDebtQueryRes;
import com.jrx.anytxn.transaction.dto.payment.DelinquencyDebtQueryRes;
import com.jrx.anytxn.transaction.dto.payment.DelinquencyPaymentReq;
import com.jrx.anytxn.transaction.dto.payment.DelinquencyTrialReq;
import com.jrx.anytxn.transaction.dto.payment.DelinquencyTrialRes;
import com.jrx.anytxn.transaction.dto.payment.EtlSettleReq;
import com.jrx.anytxn.transaction.dto.payment.EtlSettleRes;
import com.jrx.anytxn.transaction.dto.payment.HandlePaymentReq;
import com.jrx.anytxn.transaction.dto.payment.HandleSkipPayReq;
import com.jrx.anytxn.transaction.dto.payment.HandleSkipPayRes;
import com.jrx.anytxn.transaction.dto.payment.LoanPayOffDetailRes;
import com.jrx.anytxn.transaction.dto.payment.LoanRepaymentQueryReq;
import com.jrx.anytxn.transaction.dto.payment.LoanRepaymentQueryRes;
import com.jrx.anytxn.transaction.dto.payment.LoanRepaymentVo;
import com.jrx.anytxn.transaction.dto.payment.LoanStagingRes;
import com.jrx.anytxn.transaction.dto.payment.LoanStagingTrailReq;
import com.jrx.anytxn.transaction.dto.payment.LoanStagingTrailRes;
import com.jrx.anytxn.transaction.dto.payment.OnDuePayReq;
import com.jrx.anytxn.transaction.dto.payment.OnDuePayTrialReq;
import com.jrx.anytxn.transaction.dto.payment.OnDuePayTrialRes;
import com.jrx.anytxn.transaction.dto.payment.PaymentAccessCheckReq;
import com.jrx.anytxn.transaction.dto.payment.PaymentAccessCheckRes;
import com.jrx.anytxn.transaction.dto.payment.PaymentRecordDetailRes;
import com.jrx.anytxn.transaction.dto.payment.PaymentRecordQueryReq;
import com.jrx.anytxn.transaction.dto.payment.PaymentRecordQueryRes;
import com.jrx.anytxn.transaction.dto.payment.PaymentReq;
import com.jrx.anytxn.transaction.dto.payment.PaymentRes;
import com.jrx.anytxn.transaction.dto.payment.PaymentStatusQueryReq;
import com.jrx.anytxn.transaction.dto.payment.PaymentStatusQueryRes;
import com.jrx.anytxn.transaction.dto.payment.PaymentTrialReq;
import com.jrx.anytxn.transaction.dto.payment.PrePayReq;
import com.jrx.anytxn.transaction.dto.payment.PrePayRes;
import com.jrx.anytxn.transaction.dto.payment.PrePayTrialReq;
import com.jrx.anytxn.transaction.dto.payment.PrePayTrialRes;
import com.jrx.anytxn.transaction.dto.payment.RefundReq;
import com.jrx.anytxn.transaction.dto.payment.RefundRes;
import com.jrx.anytxn.transaction.dto.payment.Repayment;
import com.jrx.anytxn.transaction.dto.payment.StagingLoanRes;
import com.jrx.anytxn.transaction.dto.payment.ThirdPartyPaymentQueryReq;
import com.jrx.anytxn.transaction.dto.payment.ThirdPartyPaymentQueryRes;
import com.jrx.anytxn.transaction.dto.payment.ThirdPartyPaymentRep;
import com.jrx.anytxn.transaction.dto.payment.ThirdPartyPaymentRes;
import com.jrx.anytxn.transaction.dto.transactionLog.TlPaymentLogRes;
import com.jrx.anytxn.transaction.dto.transactionLog.TlPaymentMappingRes;
import com.jrx.anytxn.transaction.entity.AmLoanAccountInfo;
import com.jrx.anytxn.transaction.entity.AmLoanMainInfo;
import com.jrx.anytxn.transaction.entity.AmRepaymentPlanInfo;
import com.jrx.anytxn.transaction.entity.AmStageRepaymentPlanInfo;
import com.jrx.anytxn.transaction.entity.TlLoanStagingMapping;
import com.jrx.anytxn.transaction.entity.TlMistakeLog;
import com.jrx.anytxn.transaction.entity.TlPaymentAllocationLog;
import com.jrx.anytxn.transaction.entity.TlPaymentLog;
import com.jrx.anytxn.transaction.entity.TlPaymentMapping;
import com.jrx.anytxn.transaction.entity.TlThirdPartyPayment;
import com.jrx.anytxn.transaction.entity.TlTransactionInfo;
import com.jrx.anytxn.transaction.entity.TlWaivePaymentLog;
import com.jrx.anytxn.transaction.entity.TlWaivePaymentMapping;
import com.jrx.anytxn.transaction.handler.ILoanHandler;
import com.jrx.anytxn.transaction.handler.IPaymentHandler;
import com.jrx.anytxn.transaction.mapper.base.TlPaymentLogMapper;
import com.jrx.anytxn.transaction.mapper.base.TlPaymentMappingMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmLoanAccountInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmLoanMainInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmRepaymentPlanInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmStageRepaymentPlanInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtBtAutoPaymentListMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlDisbursementLogMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlLoanStagingMappingMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlMistakeLogMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlPaymentAllocationLogMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlPaymentLogMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlPaymentMappingMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlThirdPartyPaymentMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlTransactionInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlWaivePaymentLogMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlWaivePaymentMappingMapper;
import com.jrx.anytxn.transaction.service.ICustBatchService;
import com.jrx.anytxn.transaction.service.ICustomerOrderService;
import com.jrx.anytxn.transaction.service.IDealPaymentService;
import com.jrx.anytxn.transaction.service.IDealWaivePaymentService;
import com.jrx.anytxn.transaction.service.IDebtCalculationService;
import com.jrx.anytxn.transaction.service.ILoanAccountMainInfoService;
import com.jrx.anytxn.transaction.service.ILoanMainInfoService;
import com.jrx.anytxn.transaction.service.ILoanService;
import com.jrx.anytxn.transaction.service.IMistakeService;
import com.jrx.anytxn.transaction.service.IPaymentLogService;
import com.jrx.anytxn.transaction.service.IPaymentService;
import com.jrx.anytxn.transaction.service.IRepaymentPlanService;
import com.jrx.anytxn.transaction.service.ISmsService;
import com.jrx.anytxn.transaction.service.IThirdPartyPaymentService;
import com.jrx.anytxn.transaction.service.impl.PaymentMappingServiceImpl;
import org.apache.commons.collections.CollectionUtils;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.redisson.api.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 还款handler实现
 *
 * @author zhenghaopu
 * date: 2019/9/24
 */
@Component
public class PaymentHandlerImpl implements IPaymentHandler {


    private static final Logger logger = LoggerFactory.getLogger(PaymentHandlerImpl.class);

    @Autowired
    private ICustomerService customerService;
    @Autowired
    private IPrPreterminationFeeTableService prPreterminationFeeTableService;
    @Autowired
    private IBankCardService bankCardService;
    @Autowired
    private IPaymentService paymentService;
    @Autowired
    private IOrganizationTableService organizationTableService;
    @Resource
    private ExtTlPaymentLogMapper extTlPaymentLogMapper;
    @Resource
    private ExtTlPaymentMappingMapper extTlPaymentMappingMapper;
    @Resource
    private ExtAmLoanMainInfoMapper extAmLoanMainInfoMapper;
    @Autowired
    private IDealPaymentService dealPaymentService;
    @Autowired
    private IMistakeService mistakeService;
    @Resource
    private ExtTlMistakeLogMapper extTlMistakeLogMapper;
    @Autowired
    private ILoanMainInfoService loanMainInfoService;
    @Autowired
    private ILoanAccountMainInfoService loanAccountInfoService;
    @Resource
    private ExtTlDisbursementLogMapper extTlDisbursementLogMapper;
    @Resource
    private ExtTlPaymentAllocationLogMapper extTlPaymentAllocationLogMapper;
    @Resource
    private ExtAmLoanAccountInfoMapper extAmLoanAccountInfoMapper;
    @Resource
    private ExtPrOrganizationTableMapper extPrOrganizationTableMapper;
    @Autowired
    private IDebtCalculationService debtCalculationService;
    @Resource
    private ExtBtBatchStatusMapper extBtBatchStatusMapper;
    @Autowired
    private ISmsService iSmsService;
    @Resource
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private ILoanService loanService;
    @Autowired
    private IProductTableService productTableService;
    @Autowired
    private ILoanHandler loanHandler;
    @Resource
    private ExtCmCustomerBankcardInfoMapper extCmCustomerBankcardInfoMapper;
    @Autowired
    private ISegmentService segmentService;
    @Resource
    private ExtTlLoanStagingMappingMapper extTlLoanStagingMappingMapper;
    @Resource
    private ExtTlWaivePaymentMappingMapper extTlWaivePaymentMappingMapper;
    @Autowired
    private IDealWaivePaymentService dealWaivePaymentService;
    @Resource
    private ExtTlWaivePaymentLogMapper extTlWaivePaymentLogMapper;
    @Autowired
    private ISystemParameterService systemParameterService;
    @Resource
    private TlPaymentLogMapper tlPaymentLogMapper;
    @Resource
    private TlPaymentMappingMapper tlPaymentMappingMapper;
    @Autowired
    private IPaymentLogService paymentLogService;
    @Resource
    private ExtTlThirdPartyPaymentMapper extTlThirdPartyPaymentMapper;
    @Autowired
    private ILoanAccountMainInfoService loanAccountMainInfoService;
    @Resource
    private ExtAmRepaymentPlanInfoMapper extAmRepaymentPlanInfoMapper;
    @Autowired
    private IThirdPartyPaymentService thirdPartyPaymentService;
    @Autowired
    private IPrPrepaymentFeeService prPrepaymentFeeService;
    @Resource
    private ExtBtAutoPaymentListMapper extBtAutoPaymentListMapper;
    @Autowired
    private ICustBatchService custBatchService;
    @Autowired
    private IPmMerchantTableService pmMerchantTableService;
    @Autowired
    private RedissonLockService redissonLockService;
    @Autowired
    private GrayVersionConfig grayVersionConfig;
    @Autowired
    private ILoanHandler loanHandlerImpl;
    @Autowired
    private IProductTableService productTableServiceImpl;
    @Autowired
    private IRepaymentPlanService repaymentPlanService;
    @Autowired
    private IAviatorService aviatorService;
    @Autowired
    private IInterestTableService interestTableService;
    @Resource
    private ExtAmStageRepaymentPlanInfoMapper extAmStageRepaymentPlanInfoMapper;
    @Resource
    private ExtTlTransactionInfoMapper extTlTransactionInfoMapper;
    @Resource
    private IUserApiService userApiService;
    @Resource
    private PaymentMappingServiceImpl paymentMappingService;
    @Resource
    private ExtCmCustomerExtInfoMapper extCmCustomerExtInfoMapper;
    @Resource
    private ICustomerOrderService iCustomerOrderService;
    @Resource
    private ExtPrCodeTableMapper extPrCodeTableMapper;

    @Override
    public AdvanceSettleTrialRes preterminationTrail(PaymentTrialReq paymentTrialReq) throws TxnException {
        logger.debug("1.验证及获取客户信息，用户号：{}", paymentTrialReq.getUserId());
        CmCustomerSecondLevel customerRes = customerService.getSecondLevelByCondition(paymentTrialReq.getUserId(), paymentTrialReq.getChannel(),
                paymentTrialReq.getTenantId(), null);
        // 客户日终账务处理
        custBatchService.dealCustBatch(customerRes.getCustomerId(), customerRes.getOrganizationId(), customerRes.getTenantId());

        List<AmLoanMainInfo> loanMainInfos = new ArrayList<>(paymentTrialReq.getLoanList().size());
        List<AmLoanAccountInfo> loanAccountInfos = new ArrayList<>(paymentTrialReq.getLoanList().size());
        logger.debug("遍历订单信息list,查询订单信息");

        for (PaymentLoanInfoBean paymentLoanInfoBean : paymentTrialReq.getLoanList()) {
            AmLoanMainInfo loanMainInfo = getLoanInfo(paymentTrialReq, customerRes.getCustomerId(), paymentLoanInfoBean.getLoanId(),
                    paymentLoanInfoBean.getContractNumber());

            AmLoanAccountInfo amLoanAccountInfo = loanAccountInfoService.getAmLoanAccountInfoByLoanIdAndCusId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId());
            logger.debug("合同号->{}", loanMainInfo.getContractNumber());

            loanService.checkLoanStatus(amLoanAccountInfo);

            //获取提前结清信息，判断是否允许提前结清
            getPrPreterminationFeeTableRes(loanMainInfo, amLoanAccountInfo);
            loanMainInfos.add(loanMainInfo);
            loanAccountInfos.add(amLoanAccountInfo);
        }
        return paymentService.preterminationTrail(loanMainInfos, loanAccountInfos, paymentTrialReq);
    }

    @Override
    public DelinquencyTrialRes overdueTrail(DelinquencyTrialReq paymentTrialReq) throws TxnException {
        logger.debug("1.验证及获取客户信息，用户号：{}", paymentTrialReq.getUserId());
        CmCustomerSecondLevel customerRes = customerService.getSecondLevelByCondition(paymentTrialReq.getUserId(), paymentTrialReq.getChannel(),
                paymentTrialReq.getTenantId(), null);
        // 客户日终账务处理
        custBatchService.dealCustBatch(customerRes.getCustomerId(), customerRes.getOrganizationId(), customerRes.getTenantId());

        List<AmLoanAccountInfo> loanAccountInfos = new ArrayList<>(paymentTrialReq.getLoanList().size());
        List<AmLoanMainInfo> loanMainInfos = new ArrayList<>(paymentTrialReq.getLoanList().size());
        logger.debug("遍历订单信息list,查询订单信息");
        for (PaymentLoanInfoBean paymentLoanInfoBean : paymentTrialReq.getLoanList()) {
            AmLoanMainInfo loanMainInfo = getLoanInfo(paymentTrialReq, customerRes.getCustomerId(), paymentLoanInfoBean.getLoanId(),
                    paymentLoanInfoBean.getContractNumber());

            AmLoanAccountInfo amLoanAccountInfo = loanAccountInfoService.getAmLoanAccountInfoByLoanIdAndCusId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId());
            logger.debug("合同号->{}", loanMainInfo.getContractNumber());

            loanService.checkLoanStatus(amLoanAccountInfo);
            loanAccountInfos.add(amLoanAccountInfo);
            loanMainInfos.add(loanMainInfo);
        }
        return paymentService.overdueTrail(loanMainInfos, loanAccountInfos, paymentTrialReq);
    }

    @Override
    public PaymentRes preterminationRepayment(PaymentReq paymentReq) throws TxnException {

        PaymentRes paymentRes = BeanMapping.copy(paymentReq, PaymentRes.class);
        //校验请求报文以及获取机构 客户  银行 订单 等信息
        RepaymentParamBean repaymentParamBean = commonRepayment(paymentReq, paymentReq.getCardNumber());
        //客户号
        String customerId = repaymentParamBean.getCustomer().getSecondLevel().getCustomerId();
        //获取差错订单号列表
        List<String> loanIdList = getMistakeLoanIdList(customerId, paymentReq.getTenantId());

        //幂等性校验
        TlPaymentLog log = extTlPaymentLogMapper.selectByPayId(paymentReq.getSeqNo(), customerId, paymentReq.getTenantId());
        if (null != log) {
            paymentRes.setPaymentSeqNo(log.getSeqNo());
            return paymentRes;
        }

        List<AmLoanMainInfo> loanMainInfos = new ArrayList<>(paymentReq.getLoanList().size());
        List<AmLoanAccountInfo> loanAccountInfos = new ArrayList<>(paymentReq.getLoanList().size());
        Date bussinessDate = repaymentParamBean.getOrg().getNextProcessingDate();
        logger.debug("遍历订单信息list,查询订单信息");
        for (PaymentLoanInfoBean paymentLoanInfoBean : paymentReq.getLoanList()) {
            AmLoanMainInfo loanMainInfo = getLoanMainInfo(paymentReq.getTenantId(),
                    customerId, paymentLoanInfoBean.getLoanId(), paymentLoanInfoBean.getContractNumber(), paymentReq.getChannel());
            //获取订单账户
            AmLoanAccountInfo amLoanAccountInfo = loanAccountInfoService.getAmLoanAccountInfoByLoanIdAndCusId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId());
            logger.debug("合同号->{}", loanMainInfo.getContractNumber());

            loanService.checkLoanStatus(amLoanAccountInfo);

            //验证订单是否允许提前还款
            validatePayment(loanMainInfo, amLoanAccountInfo, bussinessDate, loanIdList);
            //获取提前结清信息，判断是否允许提前结清
            getPrPreterminationFeeTableRes(loanMainInfo, amLoanAccountInfo);
            loanAccountInfos.add(amLoanAccountInfo);
            loanMainInfos.add(loanMainInfo);
        }
        repaymentParamBean.setLoanMainInfoList(loanMainInfos);
        repaymentParamBean.setLoanAccountInfoList(loanAccountInfos);

        logger.debug("计算欠款并生成还款流水信息");
        String paymentSeqNo = paymentService.preterminationRepayment(paymentReq, repaymentParamBean, bussinessDate);
        paymentRes.setPaymentSeqNo(paymentSeqNo);
        return paymentRes;

    }

    @Override
    public PaymentRes overdueRepayment(DelinquencyPaymentReq delinquencyPaymentReq) throws TxnException {

        PaymentRes paymentRes = BeanMapping.copy(delinquencyPaymentReq, PaymentRes.class);
        //校验请求报文以及获取机构 客户  银行 订单 等信息
        RepaymentParamBean repaymentParamBean = commonRepayment(delinquencyPaymentReq, delinquencyPaymentReq.getCardNumber());
        //客户号
        String customerId = repaymentParamBean.getCustomer().getSecondLevel().getCustomerId();
        //获取差错订单号列表
        List<String> loanIdList = getMistakeLoanIdList(customerId, delinquencyPaymentReq.getTenantId());

        //幂等性校验
        TlPaymentLog log = extTlPaymentLogMapper.selectByPayId(delinquencyPaymentReq.getSeqNo(), customerId, delinquencyPaymentReq.getTenantId());
        if (null != log) {
            paymentRes.setPaymentSeqNo(log.getSeqNo());
            return paymentRes;
        }

        List<AmLoanAccountInfo> loanAccountInfos = new ArrayList<>(delinquencyPaymentReq.getLoanList().size());
        Date bussinessDate = repaymentParamBean.getOrg().getNextProcessingDate();
        logger.debug("遍历订单信息list,查询订单信息");
        for (PaymentLoanInfoBean paymentLoanInfoBean : delinquencyPaymentReq.getLoanList()) {
            AmLoanMainInfo loanMainInfo = getLoanMainInfo(delinquencyPaymentReq.getTenantId(),
                    customerId, paymentLoanInfoBean.getLoanId(), paymentLoanInfoBean.getContractNumber(), delinquencyPaymentReq.getChannel());
            logger.debug("合同号->{}", loanMainInfo.getContractNumber());
            //获取订单账户
            AmLoanAccountInfo amLoanAccountInfo = loanAccountInfoService.getAmLoanAccountInfoByLoanIdAndCusId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId());

            loanService.checkLoanStatus(amLoanAccountInfo);


            //验证订单是否允许还款
            validatePayment(loanMainInfo, amLoanAccountInfo, bussinessDate, loanIdList);

            //验证订单是否允许逾期还款
            validateOverPay(amLoanAccountInfo);
            loanAccountInfos.add(amLoanAccountInfo);
        }
        repaymentParamBean.setLoanAccountInfoList(loanAccountInfos);
        logger.debug("计算欠款并生成还款流水信息");
        String paymentSeqNo = paymentService.overdueRepayment(delinquencyPaymentReq, repaymentParamBean, bussinessDate);
        paymentRes.setPaymentSeqNo(paymentSeqNo);
        return paymentRes;

    }

    @Override
    public PaymentStatusQueryRes getPaymentStatus(PaymentStatusQueryReq paymentStatusQueryReq) throws TxnBizException {
        logger.debug("1.获取客户信息");
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(paymentStatusQueryReq.getUserId(),
                paymentStatusQueryReq.getChannel(), paymentStatusQueryReq.getTenantId(), null);
        return paymentService.getPaymentStatus(paymentStatusQueryReq, customer.getCustomerId());
    }

    @Override
    public void paymentAllocation(TlPaymentLog tlPaymentLog) throws Exception {
        //判断是否存在差错
        TlMistakeLog mistakeLog = extTlMistakeLogMapper.selectMistake(tlPaymentLog.getSeqNo(), tlPaymentLog.getCustomerId(),
                tlPaymentLog.getTenantId(), TransBizConstant.MIS_STATUS_0);
        if (null != mistakeLog) {
            return;
        }

        //过滤掉代偿回购
        if (TransBizConstant.REPAYMENT_TYPE_CMPS.equals(tlPaymentLog.getPaymentType())
                || TransBizConstant.REPAYMENT_TYPE_CRT.equals(tlPaymentLog.getPaymentType())) {
            return;
        }

        Date bussinessDate = null;
        try {
            PrOrganizationTable organization =
                    organizationTableService.findByOrId(tlPaymentLog.getOrganizationId(), tlPaymentLog.getTenantId());
            bussinessDate = organization.getNextProcessingDate();
            this.dealAllocation(tlPaymentLog, bussinessDate);
        } catch (Exception e) {
            //还款出现异常保存差错，后续进行差错处理
            logger.error("还款分配发生异常", e);
            mistakeLog = mistakeService.buildMistakeLog(tlPaymentLog, bussinessDate, TransBizConstant.MIS_TYPE_1);
            extTlMistakeLogMapper.insertSelective(mistakeLog);
            return;
        }

        // mq发送还款流水给出资方
        if (TransBizConstant.YES_FLAG.equals(tlPaymentLog.getJointLoanFlag())) {
            try {
                this.sendMqPaymentLog(TransBizConstant.MQ_INVESTOR_PAYMENT_TOPIC + grayVersionConfig.getCurrentVersion(), tlPaymentLog);
            } catch (Exception e) {
                logger.error("mq发送还款流水给出资方异常：{}", e);
                mistakeLog = mistakeService.buildMistakeLog(tlPaymentLog, bussinessDate, TransBizConstant.MIS_TYPE_5);
                extTlMistakeLogMapper.insertSelective(mistakeLog);
                return;
            }
        }

        logger.info("更新还款流水");
        //更新流水表状态
        TlPaymentLog updateLog = new TlPaymentLog();
        updateLog.setId(tlPaymentLog.getId());
        updateLog.setCustomerId(tlPaymentLog.getCustomerId());
        updateLog.setTenantId(tlPaymentLog.getTenantId());
        updateLog.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_S);
        extTlPaymentLogMapper.updateByIdAndCustomerIdAndTenantId(updateLog);

        //短信处理 需要根据sum_status 获取对应的模板
        tlPaymentLog.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_S);
        try {
            iSmsService.paymentProcess(tlPaymentLog);
        } catch (Exception e) {
            logger.error("还款分配，短信发送异常", e);
        }
        if (BizTypeProducts.PRODUCT_XYJY.equals(tlPaymentLog.getProductId())) {
            try {
                logger.info("还款分配，借据结清通知信息集团tlPaymentLog:{}" + tlPaymentLog);
                LoanPayOffReq loanPayOffReq = new LoanPayOffReq();
                String userId = customerService.getCustomerByChannelAndCustomerIdAndTensnId(tlPaymentLog.getChannel(), tlPaymentLog.getCustomerId(), tlPaymentLog.getTenantId()).getSecondLevel().getUserId();
                loanPayOffReq.setUserId(userId);
                loanPayOffReq.setChannelNo(tlPaymentLog.getChannel());
                loanPayOffReq.setTenantId(tlPaymentLog.getTenantId());
                loanPayOffReq.setSeqNo(tlPaymentLog.getSeqNo());
                String json = JsonUtils.obj2json(loanPayOffReq);
                userApiService.loanPayOffNotify(json);
            } catch (Exception e) {
                logger.error("还款分配，借据结清通知信息集团", e);
            }
        }

        logger.info("还款分配结束");
    }

    @Override
    public void dealAllocation(TlPaymentLog tlPaymentLog, Date bussinessDate) throws Exception {
        logger.info("开始还款分配");
        List<TlPaymentAllocationLog> tlPaymentAllocationLogList = extTlPaymentAllocationLogMapper.findAllocationLogs(tlPaymentLog.getSeqNo(), tlPaymentLog.getCustomerId(), tlPaymentLog.getTenantId());
        if (CollectionUtils.isNotEmpty(tlPaymentAllocationLogList)) {
            return;
        }

        //根据还款流水查询还款流水与订单映射信息
        List<TlPaymentMapping> tlPaymentMappingList =
                extTlPaymentMappingMapper.selectByPaymentSeqAndCustomerId(tlPaymentLog.getSeqNo(),
                        tlPaymentLog.getCustomerId(), tlPaymentLog.getTenantId());
        List<AmLoanMainInfo> loanList = new ArrayList<>();
        if (CollectionUtils.isEmpty(tlPaymentMappingList)) {
            logger.error(TransErrMsgConstant.NO_PAYMENT_MAPPING);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        //查询订单信息
        for (TlPaymentMapping mapping : tlPaymentMappingList) {
            logger.info("还款分配关联，mapping:{}", mapping);
            AmLoanMainInfo loanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndCusId(mapping.getLoanId(),
                    tlPaymentLog.getCustomerId(), tlPaymentLog.getTenantId());
            logger.info("订单，loanMainInfo:{}", loanMainInfo);
            loanList.add(loanMainInfo);
        }

        //按订单时间先后顺序排序
        loanList = loanList.stream().sorted(Comparator.comparing(AmLoanMainInfo::getPostingDate)).collect(Collectors.toList());

        String lockKey = String.format(CustomerConstant.LOAN_REDIS_LOCK, tlPaymentLog.getTenantId(), tlPaymentLog.getCustomerId());
        logger.info("RedissionLock key:{}", lockKey);
        RLock lock = redissonLockService.getRLock(lockKey, LockType.REENTRANT_LOCK);
        try {
            boolean lockFlag = lock.tryLock(CustomerConstant.LOAN_REDIS_LOCK_WAITE_TIME,
                    CustomerConstant.LOAN_REDIS_LOCK_LESS_TIME, TimeUnit.SECONDS);
            logger.info("获取RedissionLock key:{},lockFlag:{}", lockKey, lockFlag);
            if (!lockFlag) {
                logger.error(TxnRespCode.TRANSACTION_PROCESSING.getMsg());
                throw new TxnBizException(TxnRespCode.TRANSACTION_PROCESSING.getCode(), TxnRespCode.TRANSACTION_PROCESSING.getMsg());
            }
            String paymentType = tlPaymentLog.getPaymentType();
            if (TransBizConstant.REPAYMENT_TYPE_PREPAY.equals(paymentType)) {
                //提前还款
                dealPaymentService.dealPrePay(tlPaymentLog, loanList.get(0), bussinessDate);
            } else if (TransBizConstant.REPAYMENT_TYPE_PRETERMINTION.equals(paymentType)) {
                //提前结清
                dealPaymentService.dealPayOff(tlPaymentLog, loanList, bussinessDate);
            } else if (TransBizConstant.REPAYMENT_TYPE_OVERDUE.equals(paymentType)) {
                //逾期还款
                dealPaymentService.dealOverPay(tlPaymentLog, loanList, bussinessDate);
            } else if (TransBizConstant.REPAYMENT_TYPE_AUTO.equals(paymentType)) {
                //批扣
                dealPaymentService.dealPay(tlPaymentLog, loanList, bussinessDate);
            } else if (TransBizConstant.REPAYMENT_TYPE_PAY.equals(paymentType)) {
                //正常还款
                dealPaymentService.dealPay(tlPaymentLog, loanList, bussinessDate);
            } else if (TransBizConstant.REPAYMENT_TYPE_CMPS.equals(paymentType)) {
                //代偿
                dealPaymentService.dealCmpsPay(tlPaymentLog, loanList, bussinessDate);
            } else if (TransBizConstant.REPAYMENT_TYPE_CRT.equals(paymentType)) {
                //回购
                dealPaymentService.dealCrtPay(tlPaymentLog, loanList, bussinessDate);
            } else if (TransBizConstant.REPAYMENT_TYPE_BILLOVERDUEPAY.equals(paymentType)) {
                //账单逾期还款
                dealPaymentService.dealBillOverDuePay(tlPaymentLog, loanList, bussinessDate);
            } else if (TransBizConstant.REPAYMENT_TYPE_BILLPAY.equals(paymentType)) {
                //账单正常还款
                dealPaymentService.dealBillPay(tlPaymentLog, loanList, bussinessDate);
            } else if (TransBizConstant.REPAYMENT_TYPE_BILLPREPAYCURR.equals(paymentType)) {
                //账单提前还当期还款
                dealPaymentService.dealBillPrepayCurr(tlPaymentLog, loanList, bussinessDate);
            } else if (TransBizConstant.REPAYMENT_TYPE_CHARGEOFF.equals(paymentType)) {
                //核销还款
                dealPaymentService.dealPay(tlPaymentLog, loanList, bussinessDate);
            } else if (TransBizConstant.REPAYMENT_TYPE_BILLPREPAY.equals(paymentType)) {
                //账单提前还款
                dealPaymentService.dealBillPrepay(tlPaymentLog, loanList, bussinessDate);
            } else if (TransBizConstant.REPAYMENT_TYPE_REFUND.equals(paymentType)) {
                //退款
                dealPaymentService.dealRefund(tlPaymentLog, loanList, bussinessDate);
            } else if (TransBizConstant.REPAYMENT_TYPE_BILLOVERDUE_PREPAYCURR.equals(paymentType)) {
                //账单逾期提前还当期
                dealPaymentService.dealBillOverPrepayCurr(tlPaymentLog, loanList, bussinessDate);
            }
        } finally {
            lock.unlock();
        }

    }

    @Override
    public PrePayTrialRes prePayTrail(PrePayTrialReq prePayTrialReq) throws TxnException {
        logger.debug("1.验证及获取客户信息，用户号：{}", prePayTrialReq.getUserId());
        CmCustomerSecondLevel customerRes = customerService.getSecondLevelByCondition(prePayTrialReq.getUserId(), prePayTrialReq.getChannel(),
                prePayTrialReq.getTenantId(), null);
        // 客户日终账务处理
        custBatchService.dealCustBatch(customerRes.getCustomerId(), customerRes.getOrganizationId(), customerRes.getTenantId());

        //校验还款本金与还款总金额
        validatePrePayAmt(prePayTrialReq.getPrincipalAmount(), prePayTrialReq.getPaymentAmount());
        String loanId = prePayTrialReq.getLoanId();
        String contractNo = prePayTrialReq.getContractNumber();
        AmLoanMainInfo loanMainInfo = getLoanInfo(prePayTrialReq, customerRes.getCustomerId(), loanId, contractNo);
        //获取订单账户
        AmLoanAccountInfo amLoanAccountInfo = loanAccountInfoService.getAmLoanAccountInfoByLoanIdAndCusId(loanMainInfo.getLoanId(),
                loanMainInfo.getCustomerId(), loanMainInfo.getTenantId());

        loanService.checkLoanStatus(amLoanAccountInfo);

        PrePayTrialRes res = paymentService.prePayTrail(loanMainInfo, amLoanAccountInfo, prePayTrialReq);
        return res;
    }

    @Override
    public PrePayRes prePay(PrePayReq prePayReq) throws TxnException {

        //校验还款本金与还款总金额
        validatePrePayAmt(prePayReq.getPrincipalAmount(), prePayReq.getPaymentAmount());
        //校验请求报文以及获取机构 客户  银行 订单 等信息
        PrePayRes prePayRes = BeanMapping.copy(prePayReq, PrePayRes.class);
        //校验请求报文以及获取机构 客户  银行 订单 等信息
        RepaymentParamBean repaymentParamBean = commonRepayment(prePayReq, prePayReq.getCardNumber());
        //客户号
        String customerId = repaymentParamBean.getCustomer().getSecondLevel().getCustomerId();
        //获取差错订单号列表
        List<String> loanIdList = getMistakeLoanIdList(customerId, prePayReq.getTenantId());
        //幂等性校验
        TlPaymentLog log = extTlPaymentLogMapper.selectByPayId(prePayReq.getSeqNo(), customerId, prePayReq.getTenantId());
        if (null != log) {
            prePayRes.setPaymentSeqNo(log.getSeqNo());
            return prePayRes;
        }
        AmLoanMainInfo loanMainInfo = getLoanMainInfo(prePayReq.getTenantId(),
                customerId, prePayReq.getLoanId(), prePayReq.getContractNumber(), prePayReq.getChannel());
        //获取订单账户
        AmLoanAccountInfo amLoanAccountInfo = loanAccountInfoService.getAmLoanAccountInfoByLoanIdAndCusId(loanMainInfo.getLoanId(),
                loanMainInfo.getCustomerId(), loanMainInfo.getTenantId());

        loanService.checkLoanStatus(amLoanAccountInfo);

        Date bussinessDate = repaymentParamBean.getOrg().getNextProcessingDate();
        //还款校验
        validatePayment(loanMainInfo, amLoanAccountInfo, bussinessDate, loanIdList);
        //提前还款校验
        validatePrePay(amLoanAccountInfo, loanMainInfo, bussinessDate);
        repaymentParamBean.setLoanMainInfoList(Collections.singletonList(loanMainInfo));
        repaymentParamBean.setLoanAccountInfoList(Collections.singletonList(amLoanAccountInfo));
        logger.debug("计算欠款并生成还款流水信息");
        String paymentSeqNo = paymentService.prePay(prePayReq, repaymentParamBean, bussinessDate);
        prePayRes.setPaymentSeqNo(paymentSeqNo);
        return prePayRes;

    }


    /**
     * 正常还款试算
     *
     * @param onDuePayTrialReq
     * @return
     * @throws TxnBizException
     */
    @Override
    public OnDuePayTrialRes onDuePayTrial(OnDuePayTrialReq onDuePayTrialReq) throws TxnException {
        logger.debug("1.验证及获取客户信息，用户号：{}", onDuePayTrialReq.getUserId());
        CmCustomerSecondLevel customerRes = customerService.getSecondLevelByCondition(onDuePayTrialReq.getUserId(), onDuePayTrialReq.getChannel(),
                onDuePayTrialReq.getTenantId(), null);
        // 客户日终账务处理
        custBatchService.dealCustBatch(customerRes.getCustomerId(), customerRes.getOrganizationId(), customerRes.getTenantId());

        List<AmLoanAccountInfo> loanAccountInfos = new ArrayList<>(onDuePayTrialReq.getLoanList().size());
        for (PaymentLoanInfoBean paymentLoanInfoBean : onDuePayTrialReq.getLoanList()) {
            AmLoanMainInfo loanMainInfo = getLoanInfo(onDuePayTrialReq, customerRes.getCustomerId(), paymentLoanInfoBean.getLoanId(),
                    paymentLoanInfoBean.getContractNumber());
            //获取订单账户
            AmLoanAccountInfo amLoanAccountInfo = loanAccountInfoService.getAmLoanAccountInfoByLoanIdAndCusId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId());

            loanService.checkLoanStatus(amLoanAccountInfo);

            loanAccountInfos.add(amLoanAccountInfo);
        }
        return paymentService.onDuePayTrial(loanAccountInfos, onDuePayTrialReq);
    }

    /**
     * 正常还款
     *
     * @param onDuePayReq
     * @return
     * @throws TxnBizException
     */
    @Override
    public PaymentRes onDuePay(OnDuePayReq onDuePayReq) throws TxnException {

        PaymentRes paymentRes = BeanMapping.copy(onDuePayReq, PaymentRes.class);
        //校验请求报文以及获取机构 客户  银行 订单 等信息
        RepaymentParamBean repaymentParamBean = commonRepayment(onDuePayReq, onDuePayReq.getCardNumber());
        //客户号
        String customerId = repaymentParamBean.getCustomer().getSecondLevel().getCustomerId();
        //获取差错订单号列表
        List<String> loanIdList = getMistakeLoanIdList(customerId, onDuePayReq.getTenantId());

        //幂等性校验
        TlPaymentLog log = extTlPaymentLogMapper.selectByPayId(onDuePayReq.getSeqNo(), customerId, onDuePayReq.getTenantId());
        if (null != log) {
            paymentRes.setPaymentSeqNo(log.getSeqNo());
            return paymentRes;
        }
        List<AmLoanAccountInfo> loanAccountInfos = new ArrayList<>(onDuePayReq.getLoanList().size());
        Date bussinessDate = repaymentParamBean.getOrg().getNextProcessingDate();
        for (PaymentLoanInfoBean paymentLoanInfoBean : onDuePayReq.getLoanList()) {
            AmLoanMainInfo loanMainInfo = getLoanMainInfo(onDuePayReq.getTenantId(),
                    customerId, paymentLoanInfoBean.getLoanId(), paymentLoanInfoBean.getContractNumber(), onDuePayReq.getChannel());
            //获取订单账户
            AmLoanAccountInfo amLoanAccountInfo = loanAccountInfoService.getAmLoanAccountInfoByLoanIdAndCusId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId());

            loanService.checkLoanStatus(amLoanAccountInfo);

            //还款校验
            validatePayment(loanMainInfo, amLoanAccountInfo, bussinessDate, loanIdList);
            //提前还款校验
            validateOnDuePay(amLoanAccountInfo, bussinessDate);
            loanAccountInfos.add(amLoanAccountInfo);
        }
        repaymentParamBean.setLoanAccountInfoList(loanAccountInfos);
        logger.debug("计算欠款并生成还款流水信息");
        String paymentSeqNo = paymentService.onDuePay(onDuePayReq, repaymentParamBean, bussinessDate);
        PaymentRes res = BeanMapping.copy(onDuePayReq, PaymentRes.class);
        res.setPaymentSeqNo(paymentSeqNo);
        return res;

    }

    @Override
    public PaymentRecordQueryRes queryPaymentRecord(PaymentRecordQueryReq req) throws TxnBizException {
        //构建贷
        // 还款记录查询返回
        PaymentRecordQueryRes res = BeanMapping.copy(req, PaymentRecordQueryRes.class);

        String tenantId = req.getTenantId();
        //查询客户信息
        CmCustomerSecondLevel customerSecondLevel = customerService.getSecondLevelByCondition(req.getUserId(), req.getChannel(), tenantId, null);
        String customerId = customerSecondLevel.getCustomerId();
        List<String> seqNoList = null;
        String loanId = req.getLoanId();
        BigDecimal loanAmount = BigDecimal.ZERO;
        // 订单编号不为空
        if (StringUtils.isNotBlank(loanId)) {
            //查询此订单信息
            AmLoanMainInfo amLoanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndCusId(req.getLoanId(), customerId, tenantId);
            loanAmount = amLoanMainInfo.getLoanAmount();
            //查询订单与还款流水映射
            seqNoList = extTlPaymentMappingMapper.selectByLoanIdAndCustomerId(loanId, customerId, tenantId);
            //若不存在还款流水信息 直接返回
            if (CollectionUtils.isEmpty(seqNoList)) {
                res.setRepaymentList(Collections.emptyList());
                return res;
            }
        }
        List<String> productIds = null;
        if (StringUtils.isNotBlank(req.getBizType())) {
            productIds = BizTypeProducts.getByBizType(BizTypeEnum.getByCode(req.getBizType()));
        }
        List<PaymentRecordDetailRes> paymentRecordDetailResList = new ArrayList<>();
        //将还款流水按还款流水号group
        if (StringUtils.isNotBlank(req.getPaymentSeqNo())) {
            String paymentSeqNo = req.getPaymentSeqNo();
            res = getPaymentRecordByPaymentSeq(tenantId, customerId, paymentSeqNo, res);
            paymentRecordDetailResList = res.getRepaymentList();
        } else {
            // 获取查询的还款状态
            List<String> statusList = getPaymentStatus(req);
            //获取还款流水（不包含失败状态）
            // Arrays.asList(TransBizConstant.PAYMENT_LOG_SUM_STATUS_D, TransBizConstant.PAYMENT_LOG_SUM_STATUS_S, TransBizConstant.PAYMENT_LOG_SUM_STATUS_O, TransBizConstant.PAYMENT_LOG_SUM_STATUS_T)
            int pageNum = Integer.parseInt(req.getPageNum());
            int pageSize = Integer.parseInt(req.getPageSize());
            Page<TlPaymentLog> page = PageHelper.startPage(pageNum, pageSize);
            extTlPaymentLogMapper.selectBySeqNoListAndStatusList(seqNoList, statusList, productIds, customerId, tenantId);
            List<TlPaymentLog> tlPaymentLogs = page.getResult();

            //总页数
            res.setTotalPage(page.getPages() + "");
            //若无还款流水
            if (CollectionUtils.isEmpty(tlPaymentLogs)) {
                res.setRepaymentList(Collections.emptyList());
                return res;
            }
            //查询还款分配流水
            TlPaymentAllocationLog paymentAllocationLog = new TlPaymentAllocationLog();
            paymentAllocationLog.setCustomerId(customerId);
            paymentAllocationLog.setTenantId(tenantId);
            paymentAllocationLog.setLoanId(loanId);
            List<TlPaymentAllocationLog> paymentAllocationLogList = extTlPaymentAllocationLogMapper.selectListByCondition(paymentAllocationLog);
            //还款分配信息map key->还款流水号 value 还款分配list
            Map<String, List<TlPaymentAllocationLog>> payAllLogMap = null;
            //若还款分配不为空 将还款分配流水按还款流水号分组
            if (CollectionUtils.isNotEmpty(paymentAllocationLogList)) {
                //还款记录详情列表
                payAllLogMap = paymentAllocationLogList.stream().collect(Collectors.groupingBy(TlPaymentAllocationLog::getSeqNo));
            }

            //遍历还款流水
            for (TlPaymentLog tlPaymentLog : tlPaymentLogs) {
                //还款记录详情
                PaymentRecordDetailRes paymentRecordDetailRes = new PaymentRecordDetailRes();
                paymentRecordDetailRes.setPaymentSeqNo(tlPaymentLog.getSeqNo());//还款交易流水号
                String sumStatus = Arrays.asList(TransBizConstant.PAYMENT_LOG_SUM_STATUS_S, TransBizConstant.PAYMENT_LOG_SUM_STATUS_F).contains(tlPaymentLog.getSumStatus())
                        ? tlPaymentLog.getSumStatus() : TransBizConstant.PAYMENT_LOG_SUM_STATUS_O ;
                paymentRecordDetailRes.setSumStatus(sumStatus);//还款状态
                paymentRecordDetailRes.setRepayDate(tlPaymentLog.getBusinessDate());//还款日期
                paymentRecordDetailRes.setLaunchTime(DateUtils.formatDateTime(tlPaymentLog.getLaunchTime()));
                paymentRecordDetailRes.setCreateTime(DateUtils.formatDateTime(tlPaymentLog.getCreateTime()));
                paymentRecordDetailRes.setRepayAmount(BigDecimal.ZERO);
                paymentRecordDetailRes.setCompleteTime(Objects.nonNull(tlPaymentLog.getCompleteTime()) ? DateUtils.formatDateTime(tlPaymentLog.getCompleteTime()) : DateUtils.formatDateTime(tlPaymentLog.getUpdateTime()));
                //是否还款分配
                if (payAllLogMap != null) {
                    List<TlPaymentAllocationLog> paymentAllocationLogs = payAllLogMap.get(tlPaymentLog.getSeqNo());
                    //有还款分配 获取详情
                    if (CollectionUtils.isNotEmpty(paymentAllocationLogs)) {
                        //将paymentAllocationLogs 转换为map key->账户类型 value 相同关联账户类型入账金额之和  过滤掉08的交易账户
                        Predicate<TlPaymentAllocationLog> paymentAllocationLogPredicate =
                                tlPaymentAllocationLog -> TransBizConstant.PAYMENT_ALLOCATION_TYPE_09.equalsIgnoreCase(tlPaymentAllocationLog.getAccountType());
                        Map<String, BigDecimal> acctMap =
                                paymentAllocationLogs.stream().filter(paymentAllocationLogPredicate).collect(Collectors.groupingBy(TlPaymentAllocationLog::getConnectAccountType,
                                        CollectorsUtils.summingBigDecimal(TlPaymentAllocationLog::getPostingAmount)));
                        //已还本金
                        BigDecimal repayPrinciple =
                                Optional.ofNullable(acctMap.get(TransBizConstant.ACCOUNT_TYPE_06)).orElse(BigDecimal.ZERO);
                        //已还费用
                        BigDecimal repayFee =
                                Optional.ofNullable(acctMap.get(TransBizConstant.ACCOUNT_TYPE_03)).orElse(BigDecimal.ZERO);
                        //已还利息
                        BigDecimal repayInterest =
                                Optional.ofNullable(acctMap.get(TransBizConstant.ACCOUNT_TYPE_04)).orElse(BigDecimal.ZERO);
                        //已还罚息
                        BigDecimal repayPenaltyInterest =
                                Optional.ofNullable(acctMap.get(TransBizConstant.ACCOUNT_TYPE_11)).orElse(BigDecimal.ZERO);
                        //已还复利
                        BigDecimal repayCompoundInterest =
                                Optional.ofNullable(acctMap.get(TransBizConstant.ACCOUNT_TYPE_12)).orElse(BigDecimal.ZERO);
                        paymentRecordDetailRes.setAutoPaymentFlag(TransBizConstant.REPAYMENT_TYPE_AUTO.equalsIgnoreCase(tlPaymentLog.getPaymentType()) ? TransBizConstant.YES_FLAG: TransBizConstant.NO_FLAG);
                        paymentRecordDetailRes.setRepayPrinciple(repayPrinciple);//已还本金
                        paymentRecordDetailRes.setRepayInterest(repayInterest); //已还利息
                        paymentRecordDetailRes.setRepayFee(repayFee);  //已还费用
                        paymentRecordDetailRes.setRepayPenaltyInterest(repayPenaltyInterest);//已还罚息
                        paymentRecordDetailRes.setRepayCompoundInterest(repayCompoundInterest);//已还复利
                        paymentRecordDetailRes.setRepayAmount(repayPrinciple.add(repayInterest).add(repayFee).add(repayPenaltyInterest).add(repayCompoundInterest));//还款总额
                        paymentRecordDetailRes.setLoanId(loanId);//借据编号
                        paymentRecordDetailRes.setLoanAmount(loanAmount);//借款金额
                    }
                }
                //if (BigDecimal.ZERO.compareTo(paymentRecordDetailRes.getRepayAmount()) != 0) {
                    paymentRecordDetailResList.add(paymentRecordDetailRes);//放入还款详情
                //}
            }
        }
        res.setRepaymentList(paymentRecordDetailResList);
        return res;
    }

    /**
     *  获取查询的还款状态
     * @param req
     * @return
     */
    public List<String> getPaymentStatus(PaymentRecordQueryReq req) {
        List<String> statusList = new ArrayList<>();
        // 0.处理中、1.成功、2.失败
        String paymentStatus = req.getPaymentStatus();
        if (StringUtils.isNotBlank(paymentStatus)) {
            switch (paymentStatus) {
                case "0":
                    statusList = Arrays.asList(TransBizConstant.PAYMENT_LOG_SUM_STATUS_D, TransBizConstant.PAYMENT_LOG_SUM_STATUS_O,
                            TransBizConstant.PAYMENT_LOG_SUM_STATUS_T, TransBizConstant.PAYMENT_LOG_SUM_STATUS_P);
                    break;
                case "1":
                    statusList = Collections.singletonList(TransBizConstant.PAYMENT_LOG_SUM_STATUS_S);
                    break;
                case "2":
                    statusList = Collections.singletonList(TransBizConstant.PAYMENT_LOG_SUM_STATUS_F);
                    break;
                default:
                    statusList = null;
            }
        } else {
            statusList = Collections.singletonList(TransBizConstant.PAYMENT_LOG_SUM_STATUS_S);
        }
        return statusList;
    }

    /**
     * 还款流水对应得还款分配详情
     * @param tenantId
     * @param customerId
     * @param paymentSeqNo
     * @param resp
     * @return
     */
    public PaymentRecordQueryRes getPaymentRecordByPaymentSeq(String tenantId, String customerId, String paymentSeqNo, PaymentRecordQueryRes resp) {
        TlPaymentLogRes log = paymentLogService.getPaymentBySeqNoAndCusId(tenantId, paymentSeqNo, customerId);
        List<PaymentRecordDetailRes> paymentRecordDetailResList = new ArrayList<>();
        if (Objects.nonNull(log)) {
            if (TransBizConstant.PAYMENT_LOG_SUM_STATUS_S.equalsIgnoreCase(log.getSumStatus())) {
                List<TlPaymentMappingRes> mapping = paymentMappingService.getPaymentMappingBySeqNoAndCusId(tenantId, customerId, paymentSeqNo);
                mapping.forEach(m -> {
                    PaymentRecordDetailRes res = new PaymentRecordDetailRes();
                    res.setRepayDate(log.getBusinessDate());
                    res.setPaymentSeqNo(paymentSeqNo);
                    res.setLoanAmount(m.getLoanAmount());
                    res.setLoanId(m.getLoanId());
                    res.setRepayAmount(m.getPaymentAmount());
                    res.setLaunchTime(DateUtils.formatDateTime(log.getCreateTime()));
                    res.setCreateTime(DateUtils.formatDateTime(log.getUpdateTime()));
                    res.setCompleteTime(DateUtils.formatDateTime(log.getUpdateTime()));
                    res.setSumStatus(log.getSumStatus());
                    res.setAutoPaymentFlag(TransBizConstant.REPAYMENT_TYPE_AUTO.equalsIgnoreCase(log.getPaymentType()) ? TransBizConstant.YES_FLAG: TransBizConstant.NO_FLAG);
                    paymentRecordDetailResList.add(res);
                });
            } else {
                CmCustomerBankcardInfo cmCustomerBankcardInfo = extCmCustomerBankcardInfoMapper.selectBankCardByCondition(customerId, TransBizConstant.Y_FLAG, null, Constants.DEFAULT_CHANNEL, tenantId);
                PaymentRecordDetailRes res = new PaymentRecordDetailRes();
                res.setBankcardNum(cmCustomerBankcardInfo.getCardNumber());
                res.setRepayAmount(log.getAmount());
                res.setLaunchTime(DateUtils.formatDateTime(log.getCreateTime()));
                res.setCreateTime(DateUtils.formatDateTime(log.getUpdateTime()));
                res.setCompleteTime(DateUtils.formatDateTime(log.getUpdateTime()));
                res.setSumStatus(log.getSumStatus());
                res.setAutoPaymentFlag(TransBizConstant.REPAYMENT_TYPE_AUTO.equalsIgnoreCase(log.getPaymentType()) ? TransBizConstant.YES_FLAG: TransBizConstant.NO_FLAG);
                paymentRecordDetailResList.add(res);
            }
            resp.setPayAmount(log.getAmount());
            resp.setPaymentBankName(log.getPayerBankName());
            resp.setPaymentCardNo(log.getPayerAccount());
            resp.setLaunchTime(DateUtils.format(log.getLaunchTime(), DateUtils.DATETIME_PATTERN));
            if (TransBizConstant.PAYMENT_LOG_SUM_STATUS_S.equals(log.getSumStatus())) {
                resp.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_S);
            } else if (TransBizConstant.PAYMENT_LOG_SUM_STATUS_F.equals(log.getSumStatus())){
                resp.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_F);
            }  else {
                resp.setSumStatus("P");
            }
        }
        resp.setRepaymentList(paymentRecordDetailResList);
        return resp;
    }

    @Override
    public DelinquencyDebtQueryRes queryDelinquencyDebt(CommonReq commonReq) throws TxnException {
        //构建逾期欠款明细返回
        DelinquencyDebtQueryRes delinquencyDebtQueryRes = BeanMapping.copy(commonReq, DelinquencyDebtQueryRes.class);
        //查询客户信息
        CmCustomerSecondLevel customerSecondLevel =
                customerService.getSecondLevelByCondition(commonReq.getUserId(), commonReq.getChannel(),
                        commonReq.getTenantId(), null);
        // 客户日终账务处理
        custBatchService.dealCustBatch(customerSecondLevel.getCustomerId(), customerSecondLevel.getOrganizationId(), customerSecondLevel.getTenantId());
        //查询客户所有逾期订单欠款信息
        delinquencyDebtQueryRes.setDelqtList(paymentService.queryDelinquencyDebtDetail(customerSecondLevel.getCustomerId(), commonReq.getTenantId(), commonReq.getChannel()));
        return delinquencyDebtQueryRes;
    }

    @Override
    public CustomerDebtQueryRes queryCustomerDebt(CommonReq commonReq) throws TxnException {
        //构建客户欠款信息
        CustomerDebtQueryRes customerDebtQueryRes = BeanMapping.copy(commonReq, CustomerDebtQueryRes.class);
        //查询客户信息
        CmCustomerSecondLevel customerSecondLevel =
                customerService.getSecondLevelByCondition(commonReq.getUserId(), commonReq.getChannel(),
                        commonReq.getTenantId(), null);

        // 客户日终账务处理
        custBatchService.dealCustBatch(customerSecondLevel.getCustomerId(), customerSecondLevel.getOrganizationId(), customerSecondLevel.getTenantId());

        //查询客户未结清订单列表
        List<String> statusList = Arrays.asList(TransBizConstant.LOAN_STATUS_0, TransBizConstant.LOAN_STATUS_1,
                TransBizConstant.LOAN_STATUS_2);
        List<AmLoanAccountInfo> loanAccountInfoList =
                extAmLoanAccountInfoMapper.selectByStatusAndChannel(commonReq.getChannel(),
                        customerSecondLevel.getCustomerId(), commonReq.getTenantId(), statusList);
        if (CollectionUtils.isNotEmpty(loanAccountInfoList)) {

            for (AmLoanAccountInfo amLoanAccountInfo : loanAccountInfoList) {
                loanService.checkLoanStatus(amLoanAccountInfo);
            }

            customerDebtQueryRes.setRemainCount(loanAccountInfoList.size());//未结清订单数
            //逾期订单数
            long delqCount =
                    loanAccountInfoList.stream().filter(amLoanAccountInfo -> TransBizConstant.LOAN_STATUS_2.equalsIgnoreCase(amLoanAccountInfo.getAccountStatus())).count();
            customerDebtQueryRes.setDelqCount((int) delqCount);//逾期订单数
            BigDecimal remainPrincipal =
                    loanAccountInfoList.stream().map(AmLoanAccountInfo::getRemainPrincipal).reduce(BigDecimal.ZERO, BigDecimal::add);
            customerDebtQueryRes.setRemainPrincipal(remainPrincipal);//本金余额
            customerDebtQueryRes.setPaymentDay(customerSecondLevel.getStatementDd());//还款日
            //查询机构
            PrOrganizationTable org =
                    extPrOrganizationTableMapper.selectByOrganizationId(customerSecondLevel.getOrganizationId(),
                            commonReq.getTenantId());
            Date businessDate = org.getNextProcessingDate();
            //过滤掉新建的订单
            Predicate<AmLoanAccountInfo> loanAccountInfoPredicate =
                    amLoanAccountInfo -> TransBizConstant.LOAN_STATUS_0.equalsIgnoreCase(amLoanAccountInfo.getAccountStatus());
            //遍历订单计算欠款
            loanAccountInfoList.stream().filter(loanAccountInfoPredicate).forEach(amLoanAccountInfo -> {
                //计算欠款
                DebtBean debtBean = debtCalculationService.getBalance(amLoanAccountInfo, businessDate, null, true, true,
                        null);
                customerDebtQueryRes.setDebtAmount(customerDebtQueryRes.getDebtAmount().add(debtBean.getTotalAmount()));//欠款
            });
        }
        return customerDebtQueryRes;
    }

    @Override
    public LoanStagingTrailRes loanStagingTrail(LoanStagingTrailReq request) throws Exception {
        // 分期时间段校验
        stageTimeCheck(request.getTenantId());
        logger.info("账单分期试算开始");
        //1 获取客户2级信息
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(request.getUserId(),
                request.getChannel(), request.getTenantId(), null);
        if (customer == null) {
            logger.error("客户信息未找到，用户编号：{}，渠道：{}", request.getUserId(), request.getChannel());
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        Integer statementDd = customer.getStatementDd();
        Integer paymentDd = customer.getPaymentDd();
        if (statementDd == null || statementDd == 0) {
            logger.info("客户还款日账单日不存在");
            return null;
        }
        String productId = request.getProductId();
        //2 获取产品信息
        PrProductTable product = productTableService.findProByProIdAndOrgId(productId,
                customer.getOrganizationId(), request.getTenantId());
        if (product == null) {
            logger.error("产品未找到，产品编号：{}", productId);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }


        String productType = product.getTableType();

        //3 获取当前业务日期
        PrOrganizationTable organization = organizationTableService.findByOrId(customer.getOrganizationId(), request.getTenantId());
        Date busiDate = organization.getNextProcessingDate();
        statementDd = this.getStatementDayByProductId(busiDate, statementDd, productId);

        //4 校验是否允许账单分期
        CurrentStatementBean bean = validateLoanStagingDate(customer, busiDate, request, product);
        logger.info("账单日:{},还款日: {}", bean.getStatementDate(), bean.getPaymentDate());

        // 客户日终账务处理
        custBatchService.dealCustBatch(customer.getCustomerId(), customer.getOrganizationId(), customer.getTenantId());
        //信用就医不支持分期
        BizTypeEnum bizType = BizTypeProducts.getBizType(product.getProductId());
        if (BizTypeEnum.BIZ_TYPE_XYJY.equals(bizType)) {
            throw new TxnBizException(TxnRespCode.ERROR.getCode(), "信用就医产品不支持分期");
        }
        List<String> productIds = BizTypeProducts.getByBizType(bizType);
        List<AmLoanMainInfo> loanMainInfoList = null;
        List<AmLoanAccountInfo> amLoanAccountInfos = null;

        if (TransBizConstant.LOAN_TYPE_3.equals(productType)) {
            //上一账单日
            Date lastStatement = DateUtils.getDateByMonth(bean.getStatementDate(), -1);
            //7 获取上一账单日~当前账单日内所有订单
            loanMainInfoList = extAmLoanMainInfoMapper.selectByPostingDateRangeAndLoanTypeProductIds(request.getTenantId(), customer.getCustomerId(), request.getChannel(),
                    lastStatement, bean.getStatementDate(), TransBizConstant.LOAN_TYPE_4, productIds);

            if (CollectionUtils.isEmpty(loanMainInfoList)) {
                logger.info("未获取到需要账单分期的订单");
                return null;
            }

            List<String> loanids = loanMainInfoList.stream().map(AmLoanMainInfo::getLoanId).collect(Collectors.toList());

            amLoanAccountInfos = extAmLoanAccountInfoMapper.selectByLoanIdsAndStatus(
                    loanids, Arrays.asList(TransBizConstant.LOAN_STATUS_1), request.getTenantId(), customer.getCustomerId());

            if (CollectionUtils.isEmpty(amLoanAccountInfos)) {
                logger.info("未获取到需要账单分期的订单");
                return null;
            }
        } else if (TransBizConstant.LOAN_TYPE_7.equals(productType)) {
            loanMainInfoList = new ArrayList<>();
            AmLoanMainInfo loanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndLoanType(customer.getCustomerId(), request.getStagingLoanId(), request.getTenantId(), TransBizConstant.LOAN_TYPE_4);
            if (loanMainInfo == null) {
                logger.info("未获取到需要分期的订单");
                return null;
            }
            CurrentStatementBean statementBean = loanHandler.getCurrentStatement(loanMainInfo.getProductId(), statementDd, paymentDd, loanMainInfo.getFirstPaymentDate());
            //业务日期大于订单消费后的首个账单日，说明已出账单，不允许交易分期
            if (busiDate.compareTo(statementBean.getStatementDate()) >= 0) {
                throw new TxnBizException(TxnRespCode.PAYMENT_NOT_ALLOW_TRANS_STAGING.getCode(),
                        TxnRespCode.PAYMENT_NOT_ALLOW_TRANS_STAGING.getUserTips());
            }
            loanMainInfoList.add(loanMainInfo);
            amLoanAccountInfos = new ArrayList<>();
            AmLoanAccountInfo loanAccountInfo = extAmLoanAccountInfoMapper.selectByLoanId(loanMainInfo.getLoanId(), loanMainInfo.getCustomerId(), loanMainInfo.getTenantId());

            amLoanAccountInfos.add(loanAccountInfo);
        }
        //按借款时间排序
        List<AmLoanMainInfo> finalLoanMainInfoList = loanMainInfoList;
        amLoanAccountInfos.sort((l1, l2) -> {
            AmLoanMainInfo info1 = finalLoanMainInfoList.stream().filter(info -> l1.getLoanId().equals(info.getLoanId())).findFirst().get();
            AmLoanMainInfo info2 = finalLoanMainInfoList.stream().filter(info -> l2.getLoanId().equals(info.getLoanId())).findFirst().get();
            return info1.getPostingDate().compareTo(info2.getPostingDate());
        });

        List<StagingLoanRes> stagingLoanResList = new ArrayList<>();//需要分期订单列表
        BigDecimal totalAmount = BigDecimal.ZERO;
        BigDecimal stageAmt = request.getStagingAmount();
        for (AmLoanAccountInfo info : amLoanAccountInfos) {
            //判断订单是否是 信用付 订单
            AmLoanMainInfo mainInfo = loanMainInfoList.stream().filter(main -> main.getLoanId().equals(info.getLoanId()))
                    .filter(main -> TransBizConstant.LOAN_TYPE_4.equals(main.getLoanType())).findFirst().orElse(null);
            if (mainInfo == null) {
                logger.info("非信用户订单，不做处理");
                continue;
            }

            //订单状态是否有逾期 正常外其他状态
            if (!info.getAccountStatus().equals(TransBizConstant.LOAN_STATUS_1)) {
                logger.error("订单->{}当前状态不允许还款", info.getLoanId());
                throw new TxnBizException(TxnRespCode.PAYMENT_LOAN_STATUS_NOT_ALLOW.getCode(),
                        TxnRespCode.PAYMENT_LOAN_STATUS_NOT_ALLOW.getUserTips());
            }

            //计算欠款
            DebtBean debtBean = debtCalculationService.getBalance(info, busiDate, null, null, null, false);
            BigDecimal debtAmt = debtBean.getTotalAmount();
            StagingLoanRes stagingLoanRes = new StagingLoanRes();
            stagingLoanRes.setLoanId(info.getLoanId());
            stagingLoanRes.setLoanAmount(mainInfo.getLoanAmount());//总借款金额
            stagingLoanRes.setPostingDate(mainInfo.getPostingDate());
            stagingLoanRes.setMemo(mainInfo.getMemoid());
            //获取商户信息
            if (null != mainInfo.getMerchantId()) {
                PmMerchantTable pmMerchantTable = pmMerchantTableService.getByMerchanId(mainInfo.getTenantId(), mainInfo.getMerchantId());
                stagingLoanRes.setMerchantId(pmMerchantTable.getMerchantId());
                stagingLoanRes.setMerchantName(pmMerchantTable.getMerchantName());
            }


            if (TransBizConstant.LOAN_TYPE_3.equals(productType) && null != stageAmt) {
                if (stageAmt.compareTo(debtAmt) > 0) {
                    totalAmount = totalAmount.add(debtBean.getTotalAmount());
                    stagingLoanRes.setStagingAmount(debtBean.getTotalAmount());//分期总金额
                    stagingLoanRes.setStagingPrincipal(debtBean.getPrincipalAmount());
                    stagingLoanRes.setStagingInterest(debtBean.getInterestAmount());
                    stagingLoanRes.setStagingFee(debtBean.getFeeAmount());
                    stageAmt = stageAmt.subtract(debtAmt);
                } else {
                    List<TlPaymentAllocationLog> paymentLogList = dealPaymentService.calculationPrePay(info, mainInfo,
                            busiDate, stageAmt);
                    Map<String, BigDecimal> repayMap = paymentLogList.stream().collect(
                            Collectors.groupingBy(TlPaymentAllocationLog::getConnectAccountType, CollectorsUtils.summingBigDecimal(TlPaymentAllocationLog::getPostingAmount))
                    );
                    totalAmount = totalAmount.add(stageAmt);
                    stagingLoanRes.setStagingAmount(paymentLogList.stream().map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add));//分期总金额
                    stagingLoanRes.setStagingPrincipal(Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_06)).orElse(BigDecimal.ZERO));
                    stagingLoanRes.setStagingInterest(Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_04)).orElse(BigDecimal.ZERO));
                    stagingLoanRes.setStagingFee(Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_03)).orElse(BigDecimal.ZERO));
                    stagingLoanResList.add(stagingLoanRes);
                    break;
                }
            } else {
                totalAmount = totalAmount.add(debtBean.getTotalAmount());
                stagingLoanRes.setStagingAmount(debtBean.getTotalAmount());//分期总金额
                stagingLoanRes.setStagingPrincipal(debtBean.getPrincipalAmount());
                stagingLoanRes.setStagingInterest(debtBean.getInterestAmount());
                stagingLoanRes.setStagingFee(debtBean.getFeeAmount());

            }

            stagingLoanResList.add(stagingLoanRes);
        }

        if (product.getMinAmount().compareTo(totalAmount) > 0) {
            throw new TxnBizException(TxnRespCode.PAYMENT_MIN_AMT.getCode(),
                    TxnRespCode.PAYMENT_MIN_AMT.getUserTips());
        }

        //6 调用贷款试算接口
        LoanReq loanReq = BeanMapping.copy(request, LoanReq.class);
        loanReq.setLoanAmount(totalAmount);
        loanReq.setLoanDate(busiDate);
        loanReq.setStatementDay(statementDd);
        if (TransBizConstant.LOAN_TYPE_3.equals(product.getTableType())) {
            loanReq.setPaymentDate(bean.getPaymentDate());
        }
        LoanConfirmRes loanConfirmRes = loanHandler.loanTrailHandle(loanReq);

        LoanStagingTrailRes res = BeanMapping.copy(loanConfirmRes, LoanStagingTrailRes.class);
        res.setSeqNo(request.getSeqNo());
        res.setStagingLoanResList(stagingLoanResList);

        logger.info("账单分期试算结束");

        return res;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public LoanStagingRes loanStaging(LoanStagingTrailReq request) throws Exception {
        // 分期时间段校验
        stageTimeCheck(request.getTenantId());

        logger.info("账单分期开始");
        //1 获取客户2级信息
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(request.getUserId(),
                request.getChannel(), request.getTenantId(), null);
        if (customer == null) {
            logger.error("客户信息未找到，用户编号：{}，渠道：{}", request.getUserId(), request.getChannel());
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        Integer statementDd = customer.getStatementDd();
        Integer paymentDd = customer.getPaymentDd();
        if (statementDd == null || statementDd == 0) {
            logger.info("客户账单日不存在");
            return null;
        }

        String productId = request.getProductId();
        //2 获取产品信息
        PrProductTable product = productTableService.findProByProIdAndOrgId(productId,
                customer.getOrganizationId(), request.getTenantId());
        if (product == null) {
            logger.error("产品未找到，产品编号：{}", productId);
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }


        String productType = product.getTableType();
        // 修改客户客群
        List<CmCustomerExtInfo> cmCustomerExtInfos = extCmCustomerExtInfoMapper.queryCustomerExtInfos(customer.getCustomerId(), request.getTenantId(), BizTypeProducts.getSymbolProductId(product.getProductId()));
        if (CollectionUtils.isNotEmpty(cmCustomerExtInfos)) {
            customer.setCustomerGroup(cmCustomerExtInfos.get(0).getCustomerGroup());
        }
        //3 获取当前业务日期
        PrOrganizationTable organization =
                organizationTableService.findByOrId(customer.getOrganizationId(), request.getTenantId());
        Date busiDate = organization.getNextProcessingDate();
        statementDd = this.getStatementDayByProductId(busiDate, statementDd, productId);

        //验证是否在批扣中
        validateAutoPayment(organization.getTenantId(), organization.getNextProcessingDate());

        //4 校验是否允许账单分期
        CurrentStatementBean bean = validateLoanStagingDate(customer, busiDate, request, product);
        logger.info("账单日:{},还款日: {}", bean.getStatementDate(), bean.getPaymentDate());

        //客户跑批改造
        custBatchService.dealCustBatch(customer.getCustomerId(), customer.getOrganizationId(), customer.getTenantId());

        //5 获取差错订单号列表
        List<String> loanIdList = getMistakeLoanIdList(customer.getCustomerId(), customer.getTenantId());

        //6 判断客户是否存在处理中的分期订单
        List<String> tlList = extTlLoanStagingMappingMapper.selectByCustomerIdAndSumStatus(customer.getCustomerId(), TransBizConstant.LOAN_STAGING_SUM_STATUS_D, request.getTenantId());
        if (CollectionUtils.isNotEmpty(tlList)) {
            logger.error("客户{}存在账单分期记录，不允许账单分期", customer.getCustomerId());
            throw new TxnBizException(TxnRespCode.PAYMENT_STAGING_EXIST.getCode(),
                    TxnRespCode.PAYMENT_STAGING_EXIST.getUserTips());
        }

        BizTypeEnum bizType = BizTypeProducts.getBizType(product.getProductId());
        if (BizTypeEnum.BIZ_TYPE_XYJY.equals(bizType)) {
            throw new TxnBizException(TxnRespCode.ERROR.getCode(), "信用就医产品不支持分期");
        }
        List<String> productIds = BizTypeProducts.getByBizType(bizType);

        List<AmLoanMainInfo> loanMainInfoList = null;
        List<AmLoanAccountInfo> amLoanAccountInfos = null;

        if (TransBizConstant.LOAN_TYPE_3.equals(productType)) {
            //上一账单日
            Date lastStatement = DateUtils.getDateByMonth(bean.getStatementDate(), -1);
            //7 获取上一账单日~当前账单日内所有订单
            loanMainInfoList = extAmLoanMainInfoMapper.selectByPostingDateRangeAndLoanTypeProductIds(request.getTenantId(), customer.getCustomerId(), request.getChannel(),
                    lastStatement, bean.getStatementDate(), TransBizConstant.LOAN_TYPE_4, productIds);

            if (CollectionUtils.isEmpty(loanMainInfoList)) {
                logger.info("未获取到需要账单分期的订单");
                return null;
            }

            List<String> loanids = loanMainInfoList.stream().map(AmLoanMainInfo::getLoanId).collect(Collectors.toList());

            amLoanAccountInfos = extAmLoanAccountInfoMapper.selectByLoanIdsAndStatus(
                    loanids, Collections.singletonList(TransBizConstant.LOAN_STATUS_1), request.getTenantId(), customer.getCustomerId());

            if (CollectionUtils.isEmpty(amLoanAccountInfos)) {
                logger.info("未获取到需要账单分期的订单");
                return null;
            }

            //当月已做账单分期
            if (loanMainInfoList.stream().filter(loan -> TransBizConstant.LOAN_TYPE_3.equals(loan.getLoanType()))
                    .anyMatch(loan -> loan.getPostingDate().compareTo(bean.getStatementDate()) >= 0 && loan.getPostingDate().compareTo(bean.getPaymentDate()) <= 0)) {
                throw new TxnBizException(TxnRespCode.PAYMENT_STAGING_EXIST.getCode(),
                        TxnRespCode.PAYMENT_STAGING_EXIST.getUserTips());
            }
        } else if (TransBizConstant.LOAN_TYPE_7.equals(productType)) {
            loanMainInfoList = new ArrayList<>();
            AmLoanMainInfo loanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndLoanType(customer.getCustomerId(), request.getStagingLoanId(), request.getTenantId(), TransBizConstant.LOAN_TYPE_4);
            if (loanMainInfo == null) {
                logger.info("未获取到需要分期的订单");
                return null;
            }
            CurrentStatementBean statementBean = loanHandler.getCurrentStatement(loanMainInfo.getProductId(), statementDd, paymentDd, loanMainInfo.getFirstPaymentDate());
            //业务日期大于订单消费后的首个账单日，说明已出账单，不允许交易分期
            if (busiDate.compareTo(statementBean.getStatementDate()) >= 0) {
                throw new TxnBizException(TxnRespCode.PAYMENT_NOT_ALLOW_TRANS_STAGING.getCode(),
                        TxnRespCode.PAYMENT_NOT_ALLOW_TRANS_STAGING.getUserTips());
            }
            loanMainInfoList.add(loanMainInfo);
            amLoanAccountInfos = new ArrayList<>();
            AmLoanAccountInfo loanAccountInfo = extAmLoanAccountInfoMapper.selectByLoanId(loanMainInfo.getLoanId(), loanMainInfo.getCustomerId(), loanMainInfo.getTenantId());
            if (!TransBizConstant.LOAN_STATUS_1.equals(loanAccountInfo.getAccountStatus())) {
                throw new TxnBizException(TxnRespCode.PAYMENT_LOAN_STATUS_NOT_ALLOW.getCode(),
                        TxnRespCode.PAYMENT_LOAN_STATUS_NOT_ALLOW.getUserTips());
            }
            amLoanAccountInfos.add(loanAccountInfo);
        }

        //按借款时间排序
        List<AmLoanMainInfo> finalLoanMainInfoList = loanMainInfoList;
        amLoanAccountInfos.sort((l1, l2) -> {
            AmLoanMainInfo info1 = finalLoanMainInfoList.stream().filter(info -> l1.getLoanId().equals(info.getLoanId())).findFirst().get();
            AmLoanMainInfo info2 = finalLoanMainInfoList.stream().filter(info -> l2.getLoanId().equals(info.getLoanId())).findFirst().get();
            return info1.getPostingDate().compareTo(info2.getPostingDate());
        });

        List<StagingLoanRes> stagingLoanResList = new ArrayList<>();//需要分期订单列表
        BigDecimal totalAmount = BigDecimal.ZERO;
        BigDecimal stageAmt = request.getStagingAmount();
        String loanUse = "06";//消费用途 06 其他
        for (AmLoanAccountInfo info : amLoanAccountInfos) {
            //判断订单是否是 信用付 订单
            AmLoanMainInfo mainInfo = loanMainInfoList.stream().filter(main -> main.getLoanId().equals(info.getLoanId()))
                    .filter(main -> TransBizConstant.LOAN_TYPE_4.equals(main.getLoanType())).findFirst().orElse(null);
            if (mainInfo == null) {
                logger.info("非信用户订单，不做处理");
                continue;
            }

            //账单分期校验
            validateLoanStaging(mainInfo, info, loanIdList);

            //计算欠款
            DebtBean debtBean = debtCalculationService.getBalance(info, busiDate, null, null, null, false);
            BigDecimal debtAmt = debtBean.getTotalAmount();
            StagingLoanRes stagingLoanRes = new StagingLoanRes();
            stagingLoanRes.setLoanId(info.getLoanId());
            stagingLoanRes.setLoanAmount(mainInfo.getLoanAmount());//总借款金额
            stagingLoanRes.setPostingDate(mainInfo.getPostingDate());
            stagingLoanRes.setMemo(mainInfo.getMemoid());
            //获取商户信息
            if (null != mainInfo.getMerchantId()) {
                PmMerchantTable pmMerchantTable = pmMerchantTableService.getByMerchanId(mainInfo.getTenantId(), mainInfo.getMerchantId());
                stagingLoanRes.setMerchantId(pmMerchantTable.getMerchantId());
                stagingLoanRes.setMerchantName(pmMerchantTable.getMerchantName());
            }
            if (TransBizConstant.LOAN_TYPE_3.equals(productType) && null != stageAmt) {
                if (stageAmt.compareTo(debtAmt) > 0) {
                    totalAmount = totalAmount.add(debtBean.getTotalAmount());
                    stagingLoanRes.setStagingAmount(debtBean.getTotalAmount());//分期总金额
                    stagingLoanRes.setStagingPrincipal(debtBean.getPrincipalAmount());
                    stagingLoanRes.setStagingInterest(debtBean.getInterestAmount());
                    stagingLoanRes.setStagingFee(debtBean.getFeeAmount());
                    stageAmt = stageAmt.subtract(debtAmt);
                } else {
                    List<TlPaymentAllocationLog> paymentLogList = dealPaymentService.calculationPrePay(info, mainInfo,
                            busiDate, stageAmt);
                    Map<String, BigDecimal> repayMap = paymentLogList.stream().collect(
                            Collectors.groupingBy(TlPaymentAllocationLog::getConnectAccountType, CollectorsUtils.summingBigDecimal(TlPaymentAllocationLog::getPostingAmount))
                    );
                    totalAmount = totalAmount.add(stageAmt);
                    stagingLoanRes.setStagingAmount(paymentLogList.stream().map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add));//分期总金额
                    stagingLoanRes.setStagingPrincipal(Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_06)).orElse(BigDecimal.ZERO));
                    stagingLoanRes.setStagingInterest(Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_04)).orElse(BigDecimal.ZERO));
                    stagingLoanRes.setStagingFee(Optional.ofNullable(repayMap.get(TransBizConstant.ACCOUNT_TYPE_03)).orElse(BigDecimal.ZERO));
                    stagingLoanResList.add(stagingLoanRes);
                    break;
                }
            } else {
                totalAmount = totalAmount.add(debtBean.getTotalAmount());
                stagingLoanRes.setStagingAmount(debtBean.getTotalAmount());//分期总金额
                stagingLoanRes.setStagingPrincipal(debtBean.getPrincipalAmount());
                stagingLoanRes.setStagingInterest(debtBean.getInterestAmount());
                stagingLoanRes.setStagingFee(debtBean.getFeeAmount());

            }
            stagingLoanResList.add(stagingLoanRes);
        }


        if (product.getMinAmount().compareTo(totalAmount) > 0) {
            throw new TxnBizException(TxnRespCode.PAYMENT_MIN_AMT.getCode(),
                    TxnRespCode.PAYMENT_MIN_AMT.getUserTips());
        }

        //8 查询客户默认收款卡
        List<CmCustomerBankcardInfo> cardList = extCmCustomerBankcardInfoMapper.selectByDefaultGetFlag(customer.getCustomerId(), customer.getChannel(),
                customer.getTenantId(), TransBizConstant.Y_FLAG);
        if (CollectionUtils.isEmpty(cardList)) {
            logger.error("未获取到客户默认收款卡，客户编号：{}", customer.getCustomerId());
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }

        if (StringUtils.isBlank(request.getThirdChannel())) {
            request.setThirdChannel(finalLoanMainInfoList.get(0).getThirdChannel());
        }

        if (StringUtils.isBlank(request.getSubChannel())) {
            request.setSubChannel(finalLoanMainInfoList.get(0).getSubChannel());
        }

        //9 调用贷款确认接口
        LoanConfirmReq loanReq = BeanMapping.copy(request, LoanConfirmReq.class);
        loanReq.setLoanAmount(totalAmount);
        loanReq.setLoanDate(busiDate);
        loanReq.setStatementDay(statementDd);
        loanReq.setLoanUse(loanUse);
        loanReq.setCardNumber(cardList.get(0).getCardNumber());
        String monthDes = DateUtils.format(busiDate, "yyyy年MM月账单分期");
        ;
        loanReq.setDescription(monthDes);
        if (TransBizConstant.LOAN_TYPE_3.equals(product.getTableType())) {
            loanReq.setPaymentDate(bean.getPaymentDate());
        }
        String loanId = amLoanAccountInfos.get(0).getLoanId();
        List<String> extFields = finalLoanMainInfoList.stream().filter(l -> loanId.equals(l.getLoanId())).map(AmLoanMainInfo::getExtFields).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(extFields)) {
            loanReq.setExtFields(extFields.get(0));
        }
        //账单分期不生成流水 不更新额度
        LoanConfirmRes loanConfirmRes = loanHandler.loanConfirmHandlerByLoanStaging(loanReq);

        //10 写入账单分期关联表
        createBtLoanStaging(busiDate, loanConfirmRes, stagingLoanResList, customer);

        //生成符合监管的还款计划
        AmLoanMainInfo amLoanMainInfo = BeanMapping.copy(loanConfirmRes, AmLoanMainInfo.class);
        amLoanMainInfo.setTenantId(request.getTenantId());
        amLoanMainInfo.setOrganizationId(organization.getOrganizationId());
        amLoanMainInfo.setChannel(request.getChannel());
        amLoanMainInfo.setCustomerId(customer.getCustomerId());
        amLoanMainInfo.setProductId(product.getProductId());
        List<AmStageRepaymentPlanInfo> stageRepaymentPlanInfoList = new ArrayList<>();
        for (StagingLoanRes stagingLoanRes : stagingLoanResList) {
            amLoanMainInfo.setLoanId(stagingLoanRes.getLoanId());
            BigDecimal amt = stagingLoanRes.getStagingAmount();
            if (amt.compareTo(BigDecimal.valueOf(request.getTenor()).multiply(BigDecimal.valueOf(0.01))) > 0) {
                LoanPlanParamBean loanPlanParamBean = repaymentPlanService.buildLoanPlanParamBean(amLoanMainInfo,
                        busiDate, amt, loanConfirmRes.getTenor(),
                        null, loanConfirmRes.getFirstPaymentDueDate());

                //规则匹配相关参数
                Map<String, Object> map = new HashMap<>();
                map.put(TransBizConstant.LOAN_RULE_TRANS_CHANNEL, loanReq.getChannel());//渠道
                map.put(TransBizConstant.LOAN_RULE_CUS_IDENTIFY, customer.getCustomerGroup());//客群
                map.put(TransBizConstant.LOAN_RULE_TENOR, loanReq.getTenor());//期数
                map.put(TransBizConstant.LOAN_RULE_TRANS_AMOUNT, loanReq.getLoanAmount());//借款金额
                map.put(TransBizConstant.LOAN_RULE_CUSTOMER_LEVEL, null);//客户等级
                logger.debug("获取利率表ID");
                RuleMatchReq ruleMatchReq = new RuleMatchReq(organization.getOrganizationId(),
                        loanReq.getProductId(), TransBizConstant.PRO_INTEREST_TABLE_ID, map, loanReq.getTenantId());
                String interestId =
                        (String) aviatorService.executeRuleResultMap(ruleMatchReq).get(TransBizConstant.LOAN_RULE_RES_KEY);
                //获取利率信息
                PrInterestTable prInterestTable = interestTableService.findInterestByInterestId(interestId, request.getTenantId());
                loanPlanParamBean.setInterestTable(prInterestTable);
                loanPlanParamBean.setPrProductTable(product);
                loanPlanParamBean.setMinIntervalDays(product.getStatemntInterval());
                //调用工具类生成还款计划
                List<AmRepaymentPlanInfo> amRepaymentPlanInfoList = repaymentPlanService.buildRepaymentList(loanPlanParamBean);
                List<AmStageRepaymentPlanInfo> repaymentPlanInfoList = BeanMapping.copyList(amRepaymentPlanInfoList, AmStageRepaymentPlanInfo.class);
                repaymentPlanInfoList.forEach(
                        plan -> {
                            try {
                                plan.setId(segmentService.getId(TransBizConstant.AM_STAGE_REPAYMENT_PLAN_ID));
                            } catch (SegmentException e) {
                                throw new RuntimeException(e);
                            }
                        }
                );
                stageRepaymentPlanInfoList.addAll(repaymentPlanInfoList);
            } else {
                AmLoanMainInfo loanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndCusId(amLoanMainInfo.getLoanId(), amLoanMainInfo.getCustomerId(), amLoanMainInfo.getTenantId());
                BigDecimal actualDailyRate = loanMainInfo.getActualDailyRate();
                BigDecimal dailyRate = Objects.isNull(actualDailyRate) ? BigDecimal.ZERO : actualDailyRate;
                for (int i = 1; i <= request.getTenor(); i++) {
                    AmStageRepaymentPlanInfo amStageRepaymentPlanInfo = BeanMapping.copy(amLoanMainInfo, AmStageRepaymentPlanInfo.class);
                    amStageRepaymentPlanInfo.setActualDailyRate(dailyRate);
                    amStageRepaymentPlanInfo.setIntrStartDate(amLoanMainInfo.getPostingDate());// 起息日
                    amStageRepaymentPlanInfo.setFeeAmount(BigDecimal.ZERO);// 当期费用
                    amStageRepaymentPlanInfo.setRepayPrincipal(BigDecimal.ZERO);//当期已还本金
                    amStageRepaymentPlanInfo.setRepayInterest(BigDecimal.ZERO);//当期已还利息
                    amStageRepaymentPlanInfo.setRepayFee(BigDecimal.ZERO);//当期已还费用
                    amStageRepaymentPlanInfo.setRepayPenaltyInterest(BigDecimal.ZERO);//当期已还罚息
                    amStageRepaymentPlanInfo.setRepayCompoundInterest(BigDecimal.ZERO);//当前已还复利
                    amStageRepaymentPlanInfo.setWaivedAmount(BigDecimal.ZERO);//当期免除金额
                    amStageRepaymentPlanInfo.setRecordVersionNumber(TransBizConstant.INIT_RECORD_VERSION_NUMBER);//版本号
                    amStageRepaymentPlanInfo.setCompensateFlag(TransBizConstant.NO_FLAG);//代偿标识
                    amStageRepaymentPlanInfo.setBalTransFlag(TransBizConstant.NO_FLAG);//债权转移标识
                    amStageRepaymentPlanInfo.setPlanStatus(TransBizConstant.LOAN_PLAN_STATUS_0);//状态 0 未抛账
                    amStageRepaymentPlanInfo.setDelqDays(TransBizConstant.ZERO);//延滞天数
                    try {
                        amStageRepaymentPlanInfo.setId(segmentService.getId(TransBizConstant.AM_STAGE_REPAYMENT_PLAN_ID));
                    } catch (SegmentException e) {
                        logger.error("主键生成错误", e);
                        throw new TxnBizException(TxnRespCode.ERROR.getUserTips());
                    }
                    amStageRepaymentPlanInfo.setUpdateBy(amLoanMainInfo.getCustomerId());
                    amStageRepaymentPlanInfo.setRecordVersionNumber(TransBizConstant.INIT_RECORD_VERSION_NUMBER);
                    amStageRepaymentPlanInfo.setLiabilityFlag(TransBizConstant.LIABILITY_FLAG_00);
                    amStageRepaymentPlanInfo.setBillingTenor(i);
                    if (i == 1) {
                        amStageRepaymentPlanInfo.setPaymentDueDate(loanConfirmRes.getFirstPaymentDueDate());
                    } else {
                        amStageRepaymentPlanInfo.setPaymentDueDate(DateUtils.addMonth(loanConfirmRes.getFirstPaymentDueDate(), i - 1));
                    }
                    if (amt.compareTo(BigDecimal.valueOf(0.01)) >= 0) {
                        amStageRepaymentPlanInfo.setTotalAmount(BigDecimal.valueOf(0.01));
                        amStageRepaymentPlanInfo.setPrincipalAmount(BigDecimal.valueOf(0.01));
                        amt = amt.subtract(BigDecimal.valueOf(0.01));
                    } else {
                        amStageRepaymentPlanInfo.setTotalAmount(amt);
                        amStageRepaymentPlanInfo.setPrincipalAmount(amt);
                        amt = BigDecimal.ZERO;
                    }
                    amStageRepaymentPlanInfo.setInterestAmount(BigDecimal.ZERO);
                    stageRepaymentPlanInfoList.add(amStageRepaymentPlanInfo);
                }
            }
        }

        if (CollectionUtils.isNotEmpty(stageRepaymentPlanInfoList)) {
            extAmStageRepaymentPlanInfoMapper.insertAmStageRepaymentPlanInfoList(stageRepaymentPlanInfoList);
        }

        LoanStagingRes res = BeanMapping.copy(loanConfirmRes, LoanStagingRes.class);
        res.setBusinessDate(DateUtils.format(busiDate, "yyyyMMdd"));
        res.setStagingLoanResList(stagingLoanResList);
        logger.info("账单分期结束");
        return res;


    }

    /**
     * 分期时间段校验
     * @param tenantId
     */
    public void stageTimeCheck(String tenantId) throws TxnBizException {
        // 分期时间段校验
        boolean stageFlag = true;
        PrCodeTable prCodeTable = extPrCodeTableMapper.selectByTypeIdAndCodeId("STAGING_TIME", "stagingTimePeriod", tenantId);
        if (Objects.nonNull(prCodeTable)) {
            try {
                String[] periods = prCodeTable.getCodeName().split("-");
                // 当前时间
                LocalTime nowTime = LocalTime.parse(DateUtils.formatTime());
                // 起始时间
                LocalTime startTime = LocalTime.parse(periods[0]);
                // 结束时间
                LocalTime endTime = LocalTime.parse(periods[1]);
                // 跨天需要修改判断时间区间
                if (startTime.compareTo(endTime) > 0) {
                    if ((nowTime.compareTo(startTime) >= 0 && nowTime.compareTo(LocalTime.parse("23:59:59")) <= 0)
                            || (nowTime.compareTo(LocalTime.parse("00:00:00")) >= 0 && nowTime.compareTo(endTime) <= 0)) {
                        stageFlag = false;
                    }
                }else if (nowTime.compareTo(startTime) >= 0 && nowTime.compareTo(endTime) <= 0) {
                    stageFlag = false;
                }
            } catch (Exception e) {
                e.printStackTrace();
                logger.warn("校验分期时间段出错：{}", e);
            }
        }
        if (!stageFlag) {
            String tips = TxnRespCode.PAYMENT_STAGING_TIME.getUserTips() + "，请在：" + prCodeTable.getCodeName() + "之外时间段进行分期";
            throw new TxnBizException(TxnRespCode.PAYMENT_STAGING_TIME.getCode(), tips);
        }
    }

    @Override
    public void loanStagingAllocation(String stagingLoanId) throws TxnBizException {
        logger.info("账单分期分配开始，账单分期订单号：{}", stagingLoanId);
        if (StringUtils.isBlank(stagingLoanId)) {
            return;
        }
        //根据账单分期订单号查询
        List<TlLoanStagingMapping> tlLoanStagingMappingList = extTlLoanStagingMappingMapper.selectByStagingLoanId(stagingLoanId);
        //判断是否存在差错
        int countNumber = extTlMistakeLogMapper.countMistake(tlLoanStagingMappingList.get(0).getCustomerId(),
                tlLoanStagingMappingList.get(0).getTenantId(), TransBizConstant.MIS_STATUS_0);
        if (countNumber > 0) {
            logger.info("账单分期分配失败，客户存在差错未处理，客户编号：{}", tlLoanStagingMappingList.get(0).getCustomerId());
            return;
        }

        Date bussinessDate = null;
        try {
            PrOrganizationTable organization =
                    organizationTableService.findByOrId(tlLoanStagingMappingList.get(0).getOrganizationId(),
                            tlLoanStagingMappingList.get(0).getTenantId());
            bussinessDate = organization.getNextProcessingDate();
            //账单分期分配
            dealPaymentService.dealLoanStaging(tlLoanStagingMappingList, bussinessDate);
        } catch (Exception e) {
            logger.error("账单分期分配发生异常", e);
            TlMistakeLog mistakeLog = mistakeService.buildMistakeLog(tlLoanStagingMappingList, bussinessDate);
            extTlMistakeLogMapper.insertSelective(mistakeLog);
            return;
        }

        logger.info("更新账单分期关联表");
        extTlLoanStagingMappingMapper.updateByStagingLoanId(tlLoanStagingMappingList.get(0).getTenantId(),
                tlLoanStagingMappingList.get(0).getCustomerId(), tlLoanStagingMappingList.get(0).getStagingLoanId(),
                TransBizConstant.LOAN_STAGING_SUM_STATUS_S);

        logger.info("账单分期分配结束");
    }

    @Override
    public PaymentRes billPay(BillPayReq billPayReq) throws TxnException {
        PaymentRes paymentRes = BeanMapping.copy(billPayReq, PaymentRes.class);
        //校验请求报文以及获取机构 客户  银行 订单 等信息
        RepaymentParamBean repaymentParamBean = commonRepayment(billPayReq, billPayReq.getCardNumber());
        //客户号
        String customerId = repaymentParamBean.getCustomer().getSecondLevel().getCustomerId();
        //获取差错订单号列表
        List<String> loanIdList = getMistakeLoanIdList(customerId, billPayReq.getTenantId());

        List<String> products = null;
        if (StringUtils.isNotBlank(billPayReq.getBizType())) {
            products = BizTypeProducts.getByBizType(BizTypeEnum.getByCode(billPayReq.getBizType()));
        }

        //幂等性校验
        TlPaymentLog log = extTlPaymentLogMapper.selectByProductsAndPayId(billPayReq.getSeqNo(), customerId, billPayReq.getTenantId(), products);
        if (null != log) {
            paymentRes.setPaymentSeqNo(log.getSeqNo());
            return paymentRes;
        }

        //获取订单账户 增加小额现金贷的判断
        List<AmLoanAccountInfo> loanAccountInfos =
                extAmLoanAccountInfoMapper.selectByCustomerIdAndLoanTypeProductIdsAndNotStatus(billPayReq.getTenantId(),
                        customerId, billPayReq.getChannel(), Arrays.asList(TransBizConstant.ACCOUNT_STATUS_0,
                                TransBizConstant.ACCOUNT_STATUS_6, TransBizConstant.ACCOUNT_STATUS_7,
                                TransBizConstant.ACCOUNT_STATUS_8, TransBizConstant.ACCOUNT_STATUS_9),
                        Arrays.asList(TransBizConstant.LOAN_TYPE_2, TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_4, TransBizConstant.LOAN_TYPE_5, TransBizConstant.LOAN_TYPE_7), products);

        if (CollectionUtils.isEmpty(loanAccountInfos)) {
            logger.error(TxnRespCode.PAYMENT_LOAN_IS_EMPTY.getMsg());
            throw new TxnBizException(TxnRespCode.PAYMENT_LOAN_IS_EMPTY.getCode(), TxnRespCode.PAYMENT_LOAN_IS_EMPTY.getUserTips());
        }

        Date bussinessDate = repaymentParamBean.getOrg().getNextProcessingDate();

        for (AmLoanAccountInfo amLoanAccountInfo : loanAccountInfos) {

            AmLoanMainInfo loanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndCusId(amLoanAccountInfo.getLoanId(), customerId, amLoanAccountInfo.getTenantId());
            //还款校验
            validateBillPay(loanMainInfo, amLoanAccountInfo, loanIdList);
        }
        repaymentParamBean.setLoanAccountInfoList(loanAccountInfos);
        logger.debug("计算欠款并生成还款流水信息");
        BillResult billResult = paymentService.billPay(billPayReq, repaymentParamBean, bussinessDate);
        PaymentRes res = BeanMapping.copy(billPayReq, PaymentRes.class);
        res.setPaymentSeqNo(billResult.getSeqNo());
        List<PaymentLoanInfoBean> loanList = billResult.getLoanList().stream().map(loanId -> {
                    PaymentLoanInfoBean bean = new PaymentLoanInfoBean();
                    bean.setLoanId(loanId);
                    return bean;
                }
        ).collect(Collectors.toList());
        res.setLoanList(loanList);
        return res;

    }


    /**
     * 逾期还款校验
     *
     * @param amLoanAccountInfo
     * @throws TxnBizException
     */
    private void validateOverPay(AmLoanAccountInfo amLoanAccountInfo) throws TxnBizException {
        //订单未逾期不允许逾期还款
        if (!TransBizConstant.LOAN_STATUS_2.equals(amLoanAccountInfo.getAccountStatus())) {
            logger.error("订单->{}订单未逾期不允许逾期还款", amLoanAccountInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_NOT_OVER_DUE_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_NOT_OVER_DUE_NOT_ALLOW.getUserTips());
        }
    }

    /**
     * 正常还款校验
     *
     * @param bussinessDate
     */
    private void validateOnDuePay(AmLoanAccountInfo amLoanAccountInfo, Date bussinessDate) throws TxnBizException {
        //是否是还款日当天
        if (amLoanAccountInfo.getNextPaymentDate().compareTo(bussinessDate) != 0) {
            logger.error("订单->{}正常还款非还款日当天不允许还款", amLoanAccountInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_NOT_PAYMENT_DAY_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_NOT_PAYMENT_DAY_NOT_ALLOW.getUserTips());
        }

        //订单逾期不允许提前还款
        if (TransBizConstant.LOAN_STATUS_2.equals(amLoanAccountInfo.getAccountStatus())) {
            logger.error("订单->{}订单逾期不允许还款", amLoanAccountInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_LOAN_OVER_DUE_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_LOAN_OVER_DUE_NOT_ALLOW.getUserTips());
        }

    }

    /**
     * 提前还款校验
     *
     * @param amLoanAccountInfo
     */
    public void validatePrePay(AmLoanAccountInfo amLoanAccountInfo, AmLoanMainInfo loanMainInfo, Date bussinessDate) throws TxnBizException {
        //订单逾期不允许提前还款
        if (TransBizConstant.LOAN_STATUS_2.equals(amLoanAccountInfo.getAccountStatus())) {
            logger.error("订单->{}订单逾期不允许还款", amLoanAccountInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_LOAN_OVER_DUE_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_LOAN_OVER_DUE_NOT_ALLOW.getUserTips());
        }

        //是否是还款日当天
        if (amLoanAccountInfo.getNextPaymentDate().compareTo(bussinessDate) == 0) {
            logger.error("订单->{}提前还款还款日当天不允许还款", amLoanAccountInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_PAYMENT_DAY_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_PAYMENT_DAY_NOT_ALLOW.getUserTips());
        }

        PrPrepaymentFeeRes prPrepaymentFeeRes =
                prPrepaymentFeeService.findByPrepaymentFeeId(loanMainInfo.getPrepayTableId(),
                        loanMainInfo.getOrganizationId(), loanMainInfo.getTenantId());

        logger.debug("判断是否允许提前还款,提前还款标志->{}", prPrepaymentFeeRes.getControlOption());
        if (prPrepaymentFeeRes.getControlOption().equals(TransBizConstant.NO_FLAG)) {// 0 不允许提前结清
            //抛出异常
            logger.error("订单不允许提前还款，合同号->{}", loanMainInfo.getContractNumber());
            throw new TxnBizException(TxnRespCode.PAYMENT_PREPAY_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_PREPAY_NOT_ALLOW.getUserTips());
        }
    }

    /**
     * 通过请求获取订单
     *
     * @param req        通用请求
     * @param customerId
     * @param loanId     贷款id
     * @param contractNo 合同号
     * @return 贷款信息
     * @throws TxnBizException 自定义业务异常
     */
    private AmLoanMainInfo getLoanInfo(CommonReq req, String customerId, String loanId, String contractNo) throws TxnException {

        //验证订单号及合同号  必须传一个
        validateRepaymentTrialReq(loanId, contractNo);

        logger.debug("2.通过loanId/contractNumber查寻订单,loanId：{} contractNumber：{}", loanId, contractNo);
        AmLoanMainInfo loanMainInfo = loanMainInfoService.findLoanMainInfoByLoanIdOrContractNumber(loanId,
                contractNo, customerId, req.getTenantId(), req.getChannel());
        return loanMainInfo;
    }

    /**
     * 通用还款请求
     *
     * @param commonReq  通用请求
     * @param cardNumber 银行卡号
     * @return 还款返回
     * @throws TxnBizException 自定义业务异常
     */
    private RepaymentParamBean commonRepayment(CommonReq commonReq, String cardNumber) throws TxnBizException {
        logger.debug("1.验证及获取客户信息，用户号：{}", commonReq.getUserId());
        String tenantId = commonReq.getTenantId();
        CustomerRes customer = customerService.getCustomerByChannelAndUserId(commonReq.getChannel(),
                commonReq.getUserId(), tenantId);
        // 客户日终账务处理
        custBatchService.dealCustBatch(customer.getFirstLevel().getCustomerId(), customer.getFirstLevel().getOrganizationId(), tenantId);

        logger.debug("2.判断卡号是否存在,卡号：{}", cardNumber);
        CmCustomerBankcardInfo customerBank;
        String thirdChannel = commonReq.getThirdChannel();
        // 微信还款不需要银行卡
        if (Constant.THIRD_CHANNEL.equalsIgnoreCase(thirdChannel)) {
            customerBank = new CmCustomerBankcardInfo();
            try {
                SystemParameterRes bankIdRes = systemParameterService.findByParamCode(TransBizConstant.INNER_BANK_ID , tenantId);
                SystemParameterRes bankNameRes = systemParameterService.findByParamCode(TransBizConstant.INNER_BANK_NANE , tenantId);
                SystemParameterRes transitionalAcctRes = systemParameterService.findByParamCode(TransBizConstant.INNER_TRANSITIONAL_BANK_ACCT , tenantId);
                customerBank.setBankId(bankIdRes.getParamValue());
                customerBank.setBankName(bankNameRes.getParamValue());
                customerBank.setCardNumber(transitionalAcctRes.getParamValue());
                customerBank.setCardType(TransBizConstant.CARD_TYPE_5);
                customerBank.setTenantId(tenantId);
            } catch (TxnException e) {
                logger.error("用户{}获取还款的内部户出错了{}", commonReq.getUserId(), e);
            }
        } else {
            //若空查询默认还款卡
            if (StringUtils.isBlank(cardNumber)) {//若空查询默认还款卡
                customerBank =
                        bankCardService.getBankCardByCondition(customer.getSecondLevel().getCustomerId(), TransBizConstant.Y_FLAG,
                                null, commonReq.getChannel(), tenantId);
            } else {//否则查询卡信息
                customerBank = bankCardService.getBankCardByCondition(customer.getSecondLevel().getCustomerId(), null,
                        cardNumber, commonReq.getChannel(), tenantId);
            }
        }
        logger.debug("3.获取机构相关数据,机构id -> {}", customer.getSecondLevel().getOrganizationId());
        PrOrganizationTable org = organizationTableService.findByOrId(customer.getSecondLevel().getOrganizationId(),
                tenantId);

        //验证是否在批扣中
        Integer count = extBtAutoPaymentListMapper.selectByChannelAndCustomer(customer.getSecondLevel().getCustomerId(), commonReq.getChannel(), tenantId);
        if (count > 0) {
            validateAutoPayment(tenantId, org.getNextProcessingDate());
        }
        return new RepaymentParamBean(customerBank, customer, org);
    }

    /**
     * 获取订单信息
     *
     * @param tenantId       租户id
     * @param customerId     客户id
     * @param loanId         订单id
     * @param contractNumber 合同号
     * @return 订单信息
     * @throws TxnBizException 自定义异常
     */
    private AmLoanMainInfo getLoanMainInfo(String tenantId, String customerId, String loanId, String contractNumber, String channel) throws TxnBizException {
        //验证订单号及合同号  必须传一个
        validateRepaymentTrialReq(loanId, contractNumber);
        logger.debug("2.通过loanId/contractNumber查寻订单,loanId：{} contractNumber：{}", loanId, contractNumber);
        return loanMainInfoService.findLoanMainInfoByLoanIdOrContractNumber(loanId, contractNumber, customerId, tenantId, channel);
    }

    /**
     * 判断是否允许提前结清
     *
     * @param loanMainInfo      贷款信息
     * @param amLoanAccountInfo 贷款账户
     * @throws TxnBizException 自定义异常
     */
    private void getPrPreterminationFeeTableRes(AmLoanMainInfo loanMainInfo, AmLoanAccountInfo amLoanAccountInfo) throws TxnBizException {
        PrPreterminationFeeTableRes prPreterminationFeeTableRes =
                prPreterminationFeeTableService.findByPreterminationFeeId(loanMainInfo.getPayoffTableId(),
                        loanMainInfo.getOrganizationId(), loanMainInfo.getTenantId());

        PrOrganizationTable organization =
                organizationTableService.findByOrId(loanMainInfo.getOrganizationId(), loanMainInfo.getTenantId());
        Date bussinessDate = organization.getNextProcessingDate();

        logger.debug("判断是否允许提前结清,提前结清标志->{}", prPreterminationFeeTableRes.getControlOption());
        if (prPreterminationFeeTableRes.getControlOption().equals(TransBizConstant.NO_FLAG)) {// 0 不允许提前结清
            //抛出异常
            logger.error("订单不允许提前结清，合同号->{}", loanMainInfo.getContractNumber());
            throw new TxnBizException(TxnRespCode.PAYMENT_ADVANCE_SETTLE_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_ADVANCE_SETTLE_NOT_ALLOW.getUserTips());
        }
        logger.debug("判断是否允许逾期提前提前结清,是否允许逾期提前提前结清->{}", prPreterminationFeeTableRes.getDelqOption());
        if (TransBizConstant.NO_FLAG.equals(prPreterminationFeeTableRes.getDelqOption()) &&
                TransBizConstant.ACCOUNT_STATUS_2.equals(amLoanAccountInfo.getAccountStatus())) {
            logger.error("订单逾期不允许提前结清，合同号->{}", loanMainInfo.getContractNumber());
            throw new TxnBizException(TxnRespCode.PAYMENT_ADVANCE_SETTLE_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_ADVANCE_SETTLE_NOT_ALLOW.getUserTips());
        }
        logger.debug("判断是否允许交易日提前结清,是否允许交易日提前结清->{}", prPreterminationFeeTableRes.getLoanDayOption());
        if (TransBizConstant.NO_FLAG.equals(prPreterminationFeeTableRes.getLoanDayOption()) &&
                bussinessDate.compareTo(loanMainInfo.getPostingDate()) == 0) {
            logger.error("订单交易日不允许提前结清，合同号->{}", loanMainInfo.getContractNumber());
            throw new TxnBizException(TxnRespCode.PAYMENT_ADVANCE_SETTLE_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_ADVANCE_SETTLE_NOT_ALLOW.getUserTips());
        }

    }

    /**
     * 验证是否可以还款
     *
     * @param amLoanMainInfo 订单信息
     * @author : zhenghaopu
     * date: 2019/10/8
     */
    private void validatePayment(AmLoanMainInfo amLoanMainInfo, AmLoanAccountInfo amLoanAccountInfo,
                                 Date bussinessDate, List<String> loanIdList) throws TxnBizException {

        PrProductTable product = productTableService.findProByProIdAndOrgId(amLoanMainInfo.getProductId(),
                amLoanMainInfo.getOrganizationId(), amLoanMainInfo.getTenantId());

        //校验订单是否已经结清
        if (TransBizConstant.LOAN_STATUS_7.equals(amLoanAccountInfo.getAccountStatus()) || TransBizConstant.LOAN_STATUS_8.equals(amLoanAccountInfo.getAccountStatus())) {
            logger.error("此订单已结清,订单号{}", amLoanMainInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_DEBT_AMOUNT_ZERO.getCode(),
                    TxnRespCode.PAYMENT_DEBT_AMOUNT_ZERO.getUserTips());
        }

        //订单状态是否有逾期 正常外其他状态
        if (!amLoanAccountInfo.getAccountStatus().equals(TransBizConstant.LOAN_STATUS_1) && !amLoanAccountInfo.getAccountStatus().equals(TransBizConstant.LOAN_STATUS_2)) {
            logger.error("订单->{}当前状态不允许还款", amLoanMainInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_LOAN_STATUS_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_LOAN_STATUS_NOT_ALLOW.getUserTips());
        }
        //是否是借款日当天
        if (amLoanMainInfo.getPostingDate().compareTo(bussinessDate) == 0) {
            if (TransBizConstant.LOAN_DAY_PAY_FLAG_0.equals(product.getLoanDayPayFlag())) {
                logger.error("订单->{}借款日当前不允许还款", amLoanMainInfo.getLoanId());
                throw new TxnBizException(TxnRespCode.PAYMENT_LOAN_DAY_NOT_ALLOW.getCode(),
                        TxnRespCode.PAYMENT_LOAN_DAY_NOT_ALLOW.getUserTips());
            }
        }

        //只有下单即放款的订单才需要校验放款流水
        if (TransBizConstant.YES_FLAG.equals(product.getRemitWhenLoan())) {
            //订单是否还未放款
            boolean disFlag = disbursementLogStatus(amLoanMainInfo.getLoanId(), amLoanMainInfo.getCustomerId(),
                    amLoanMainInfo.getTenantId());
            //未放款
            if (disFlag) {
                logger.error("订单在放款处理中，不允许还款");
                throw new TxnBizException(TxnRespCode.PAYMENT_LOANING_NOT_ALLOW.getCode(),
                        TxnRespCode.PAYMENT_LOANING_NOT_ALLOW.getUserTips());
            }
        }
        //订单有是否有还款中信息
        boolean paymentFlag = repaymentLogStatusFlag(amLoanMainInfo.getLoanId(), amLoanMainInfo.getCustomerId(),
                amLoanMainInfo.getTenantId()) || repaymentWaiveLogStatusFlag(amLoanMainInfo.getLoanId(), amLoanMainInfo.getCustomerId(),
                amLoanMainInfo.getTenantId());
        if (paymentFlag) {
            logger.error("订单->{}当前还在还款中，不允许还款", amLoanMainInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_PROCESSING_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_PROCESSING_NOT_ALLOW.getUserTips());
        }
        //判断是否有差错 有差错则报错
        if (!loanIdList.isEmpty() && loanIdList.contains(amLoanMainInfo.getLoanId())) {
            logger.error("订单->{}有差错未处理", amLoanMainInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_MISTAKE_EXIST.getCode(),
                    TxnRespCode.PAYMENT_MISTAKE_EXIST.getUserTips());
        }
        TlLoanStagingMapping tlLoanStagingMapping = extTlLoanStagingMappingMapper.selectByCustIdAndOrigLoanId(amLoanMainInfo.getCustomerId(), amLoanMainInfo.getTenantId(), amLoanMainInfo.getLoanId());
        if (null != tlLoanStagingMapping && TransBizConstant.LOAN_STAGING_SUM_STATUS_D.equals(tlLoanStagingMapping.getSumStatus())) {
            logger.error("订单->{}当前还在还款中，不允许还款", amLoanMainInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_PROCESSING_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_PROCESSING_NOT_ALLOW.getUserTips());
        }


    }

    /**
     * 验证是否可以还款
     *
     * @param amLoanMainInfo
     * @param amLoanAccountInfo
     * @param loanIdList
     * @throws TxnBizException
     */
    private void validateBillPay(AmLoanMainInfo amLoanMainInfo, AmLoanAccountInfo amLoanAccountInfo, List<String> loanIdList) throws TxnBizException {

        PrProductTable product = productTableService.findProByProIdAndOrgId(amLoanMainInfo.getProductId(),
                amLoanMainInfo.getOrganizationId(), amLoanMainInfo.getTenantId());

        //订单状态是否有逾期 正常外其他状态
        if (!amLoanAccountInfo.getAccountStatus().equals(TransBizConstant.LOAN_STATUS_1) && !amLoanAccountInfo.getAccountStatus().equals(TransBizConstant.LOAN_STATUS_2)) {
            logger.error("订单->{}当前状态不允许还款", amLoanMainInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_LOAN_STATUS_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_LOAN_STATUS_NOT_ALLOW.getUserTips());
        }

        if (TransBizConstant.YES_FLAG.equals(product.getRemitWhenLoan())) {
            //订单是否还未放款
            boolean disFlag = disbursementLogStatus(amLoanMainInfo.getLoanId(), amLoanMainInfo.getCustomerId(),
                    amLoanMainInfo.getTenantId());
            //未放款
            if (disFlag) {
                logger.error("订单在放款处理中，不允许还款");
                throw new TxnBizException(TxnRespCode.PAYMENT_LOANING_NOT_ALLOW.getCode(),
                        TxnRespCode.PAYMENT_LOANING_NOT_ALLOW.getUserTips());
            }
        }
        //订单有是否有还款中信息
        boolean paymentFlag = repaymentLogStatusFlag(amLoanMainInfo.getLoanId(), amLoanMainInfo.getCustomerId(),
                amLoanMainInfo.getTenantId()) || repaymentWaiveLogStatusFlag(amLoanMainInfo.getLoanId(), amLoanMainInfo.getCustomerId(),
                amLoanMainInfo.getTenantId());
        if (paymentFlag) {
            logger.error("订单->{}当前还在还款中，不允许还款", amLoanMainInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_PROCESSING_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_PROCESSING_NOT_ALLOW.getUserTips());
        }
        //判断是否有差错 有差错则报错
        if (!loanIdList.isEmpty() && loanIdList.contains(amLoanMainInfo.getLoanId())) {
            logger.error("订单->{}有差错未处理", amLoanMainInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_MISTAKE_EXIST.getCode(),
                    TxnRespCode.PAYMENT_MISTAKE_EXIST.getUserTips());
        }
        TlLoanStagingMapping tlLoanStagingMapping = extTlLoanStagingMappingMapper.selectByCustIdAndOrigLoanId(amLoanMainInfo.getCustomerId(), amLoanMainInfo.getTenantId(), amLoanMainInfo.getLoanId());
        if (null != tlLoanStagingMapping && TransBizConstant.LOAN_STAGING_SUM_STATUS_D.equals(tlLoanStagingMapping.getSumStatus())) {
            logger.error("订单->{}当前还在还款中，不允许还款", amLoanMainInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_PROCESSING_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_PROCESSING_NOT_ALLOW.getUserTips());
        }

    }


    /**
     * 获取还款流水状态 判断其是否有最终态（S/F)的记录
     *
     * @param loanId     订单id
     * @param customerId 客户id
     * @return 是否有最终态还款流水记录
     */
    @Override
    public boolean repaymentLogStatusFlag(String loanId, String customerId, String tenantId) {
        //获取还款流水最终态（S/F)的记录
        List<TlPaymentLog> paymentLogList = extTlPaymentLogMapper.selectByCustomerIdAndSumStatus(customerId,
                new String[]{TransBizConstant.PAYMENT_LOG_SUM_STATUS_D, TransBizConstant.PAYMENT_LOG_SUM_STATUS_O,
                        TransBizConstant.PAYMENT_LOG_SUM_STATUS_T}, tenantId);
        if (CollectionUtils.isEmpty(paymentLogList)) {
            return false;
        }
        List<String> seqNoList = paymentLogList.stream().map(TlPaymentLog::getSeqNo).collect(Collectors.toList());
        List<String> loanIdList = extTlPaymentMappingMapper.selectBySeqNoList(seqNoList, customerId, tenantId);

        return loanIdList.contains(loanId);
    }

    /**
     * 获取还款流水状态 判断其是否有最终态（S/F)的记录
     *
     * @param loanId     订单id
     * @param customerId 客户id
     * @return 是否有最终态还款流水记录
     */
    @Override
    public boolean repaymentLogStatusFlagWithProducts(String loanId, String customerId, String tenantId, List<String> productIds) {
        //获取还款流水最终态（S/F)的记录
        List<TlPaymentLog> paymentLogList = extTlPaymentLogMapper.selectByCustomerIdProductsAndSumStatus(customerId,
                new String[]{TransBizConstant.PAYMENT_LOG_SUM_STATUS_D, TransBizConstant.PAYMENT_LOG_SUM_STATUS_O,
                        TransBizConstant.PAYMENT_LOG_SUM_STATUS_T}, tenantId, productIds);
        if (CollectionUtils.isEmpty(paymentLogList)) {
            return false;
        }
        List<String> seqNoList = paymentLogList.stream().map(TlPaymentLog::getSeqNo).collect(Collectors.toList());
        List<String> loanIdList = extTlPaymentMappingMapper.selectBySeqNoList(seqNoList, customerId, tenantId);

        return loanIdList.contains(loanId);
    }

    /**
     * 获取还款流水状态 判断其是否有最终态（S/F)的记录
     *
     * @param loanId     订单id
     * @param customerId 客户id
     * @return 是否有最终态还款流水记录
     */
    @Override
    public boolean repaymentWaiveLogStatusFlag(String loanId, String customerId, String tenantId) {
        //获取还款流水最终态（S/F)的记录
        List<TlWaivePaymentLog> waivePaymentLogList = extTlWaivePaymentLogMapper.selectByCustomerIdAndSumStatus(customerId,
                new String[]{TransBizConstant.PAYMENT_LOG_SUM_STATUS_D, TransBizConstant.PAYMENT_LOG_SUM_STATUS_O,
                        TransBizConstant.PAYMENT_LOG_SUM_STATUS_T}, tenantId);
        if (CollectionUtils.isEmpty(waivePaymentLogList)) {
            return false;
        }
        List<String> seqNoList = waivePaymentLogList.stream().map(TlWaivePaymentLog::getSeqNo).collect(Collectors.toList());
        List<String> loanIdList = extTlWaivePaymentMappingMapper.selectBySeqNoList(seqNoList, customerId, tenantId);

        return loanIdList.contains(loanId);
    }

    /**
     * 获取还款流水状态 判断其是否有最终态（S/F)的记录
     *
     * @param loanId     订单id
     * @param customerId 客户id
     * @return 是否有最终态还款流水记录
     */
    @Override
    public boolean repaymentWaiveLogStatusFlagWithProducts(String loanId, String customerId, String tenantId, List<String> productIds) {
        //获取还款流水最终态（S/F)的记录
        List<TlWaivePaymentLog> waivePaymentLogList = extTlWaivePaymentLogMapper.selectByCustomerIdAndSumStatus(customerId,
                new String[]{TransBizConstant.PAYMENT_LOG_SUM_STATUS_D, TransBizConstant.PAYMENT_LOG_SUM_STATUS_O,
                        TransBizConstant.PAYMENT_LOG_SUM_STATUS_T}, tenantId);
        if (CollectionUtils.isEmpty(waivePaymentLogList)) {
            return false;
        }
        List<String> seqNoList = waivePaymentLogList.stream().map(TlWaivePaymentLog::getSeqNo).collect(Collectors.toList());
        List<String> loanIdList = extTlWaivePaymentMappingMapper.selectBySeqNoAndProductsList(seqNoList, customerId, tenantId, productIds);

        return loanIdList.contains(loanId);
    }

    private boolean disbursementLogStatus(String loanId, String customerId, String tenantId) {
        List<String> statusList = Arrays.asList(TransBizConstant.PAYMENT_LOG_SUM_STATUS_D, TransBizConstant.PAYMENT_LOG_SUM_STATUS_O);
        int count = extTlDisbursementLogMapper.countDisbursementBySumStatusAndLoanId(loanId,
                customerId, tenantId, statusList);
        return count > 0;
    }

    /**
     * 订单验证
     * 订单号与外部合同号（必传一个）
     *
     * @param loanId         订单id
     * @param contractNumber 合同号
     * @throws TxnBizException 自定义业务异常
     */
    private void validateRepaymentTrialReq(String loanId, String contractNumber) throws TxnBizException {
        if (StringUtils.isBlank(loanId) && StringUtils.isBlank(contractNumber)) {
            logger.error(TransErrMsgConstant.NO_LOAN_CONTRACT);
            //贷款订单号号与外部合同号都是空
            throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(),
                    TxnRespCode.NOT_EMPTY.getUserTips());
        }
    }

    /**
     * 验证是否在批扣中
     *
     * @param tenantId 租户id
     * @throws TxnBizException 批扣中不允许还款
     */
    private void validateAutoPayment(String tenantId, Date businessDate) throws TxnBizException {
        Integer count = extBtBatchStatusMapper.countByStatusAndJobName(TransBizConstant.BATCH_SATTUS_0,
                TransBizConstant.AUTO_PAYMENT, tenantId, businessDate);
        //批扣中不允许还款
        if (count > 0) {
            logger.error(TxnRespCode.PAYMENT_AUTO_PAYMENT_PROCESSING_NOT_ALLOW.getMsg());
            throw new TxnBizException(TxnRespCode.PAYMENT_AUTO_PAYMENT_PROCESSING_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_AUTO_PAYMENT_PROCESSING_NOT_ALLOW.getUserTips());
        }
    }

    /**
     * 获取差错订单号列表
     *
     * @param customerId
     * @param tenantId
     * @return
     */
    private List<String> getMistakeLoanIdList(String customerId, String tenantId) {
        //获取差错信息对应的流水号
        List<String> paymentSeqList = extTlMistakeLogMapper.selectByStatus(customerId, tenantId, TransBizConstant.MIS_STATUS_0);
        //存在差错还款的订单
        List<String> loanIdList = Collections.emptyList();
        //存在差错对应的还款流水
        if (CollectionUtils.isNotEmpty(paymentSeqList)) {
            //获取贷款订单列表
            loanIdList = extTlPaymentMappingMapper.selectBySeqNoList(paymentSeqList, customerId, tenantId);
        }
        return loanIdList;
    }

    /**
     * 校验当前业务日期是否满足账单分期条件
     *
     * @param busiDate
     * @throws TxnBizException
     */
    private CurrentStatementBean validateLoanStagingDate(CmCustomerSecondLevel customer, Date busiDate, LoanStagingTrailReq request, PrProductTable product) throws TxnException {
        //获取当月账单日期

        int statementDay = customer.getStatementDd();
        int paymentDay = customer.getPaymentDd();

        CurrentStatementBean bean = loanHandler.getCurrentStatement(product.getProductId(), statementDay, paymentDay, busiDate);
        Date currPaymentDate = bean.getPaymentDate();

        //还款日当天加上是否批扣中
        if (busiDate.compareTo(currPaymentDate) == 0) {
            Integer count = extBtBatchStatusMapper.countByStatusAndJobName(TransBizConstant.BATCH_SATTUS_0,
                    TransBizConstant.AUTO_PAYMENT, customer.getTenantId(), busiDate);
            //批扣中不允许还款
            if (count > 0) {
                logger.error(TxnRespCode.PAYMENT_AUTO_PAYMENT_PROCESSING_NOT_ALLOW.getMsg());
                throw new TxnBizException(TxnRespCode.PAYMENT_AUTO_PAYMENT_PROCESSING_NOT_ALLOW.getCode(),
                        TxnRespCode.PAYMENT_AUTO_PAYMENT_PROCESSING_NOT_ALLOW.getUserTips());
            }
        }

        if (TransBizConstant.LOAN_TYPE_3.equals(product.getTableType())) {
            if (null != request.getStagingAmount() && product.getMinAmount().compareTo(request.getStagingAmount()) > 0) {
                throw new TxnBizException(TxnRespCode.PAYMENT_MIN_AMT.getCode(),
                        TxnRespCode.PAYMENT_MIN_AMT.getUserTips());
            }
            if (busiDate.compareTo(currPaymentDate) > 0) {
                throw new TxnBizException(TxnRespCode.PAYMENT_NOT_STAGING_DAY_ALLOW.getCode(),
                        TxnRespCode.PAYMENT_NOT_STAGING_DAY_ALLOW.getUserTips());
            }
        } else if (TransBizConstant.LOAN_TYPE_7.equals(product.getTableType())) {
            //交易分期
            if (StringUtils.isBlank(request.getStagingLoanId())) {
                throw new TxnBizException(TxnRespCode.NOT_EMPTY.getCode(), TxnRespCode.NOT_EMPTY.getUserTips());
            }
        } else {
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }


        CmCustomerFirstLevel customerFirstLevel = customerService.getFirstLevelByCondition(customer.getCustomerId(), customer.getOrganizationId(), customer.getTenantId());
        loanHandler.validateBlockCode(customerFirstLevel.getBlockCode(), customer.getTenantId());

        return bean;

    }

    /**
     * 生成订单分期关联关系表
     *
     * @param busiDate
     * @param loanConfirmRes
     */
    private void createBtLoanStaging(Date busiDate, LoanConfirmRes
            loanConfirmRes, List<StagingLoanRes> stagingLoanResList, CmCustomerSecondLevel customer) throws
            TxnBizException {
        List<TlLoanStagingMapping> mappingList = new ArrayList<>();
        for (StagingLoanRes res : stagingLoanResList) {
            TlLoanStagingMapping mapping = new TlLoanStagingMapping();
            try {
                mapping.setId(segmentService.getId(TransBizConstant.TL_LOAN_STAGING_MAPPING));
            } catch (SegmentException e) {
                logger.error("主键生成错误", e);
                throw new TxnBizException(ErrorMsgConstant.ERROR_ID);
            }
            mapping.setOrganizationId(customer.getOrganizationId());
            mapping.setTenantId(customer.getTenantId());
            mapping.setChannel(customer.getChannel());
            mapping.setCustomerId(customer.getCustomerId());
            mapping.setStagingLoanId(loanConfirmRes.getLoanId());
            mapping.setLoanAmount(loanConfirmRes.getLoanAmount());
            mapping.setBusinessDate(busiDate);
            mapping.setTotalTenor(loanConfirmRes.getTenor());
            mapping.setOriginalLoanId(res.getLoanId());
            mapping.setOriginalLoanAomount(res.getLoanAmount());
            mapping.setStagingAmount(res.getStagingAmount());
            mapping.setStagingPrincipalAmount(res.getStagingPrincipal());
            mapping.setStagingInterestAmount(res.getStagingInterest());
            mapping.setStagingFeeAmount(res.getStagingFee());
            mapping.setStagingPenaltyAmount(BigDecimal.ZERO);
            mapping.setStagingCompoundAmount(BigDecimal.ZERO);
            mapping.setSumStatus(TransBizConstant.LOAN_STAGING_SUM_STATUS_D);
            mapping.setUpdateBy(TransBizConstant.SYS_OPRATE);
            mapping.setRecordVersionNumber(TransBizConstant.INIT_RECORD_VERSION_NUMBER);
            mapping.setGrayVersion(grayVersionConfig.getCurrentVersion());
            mappingList.add(mapping);
        }

        extTlLoanStagingMappingMapper.insertTlLoanStagingMappingList(mappingList);
    }

    @Override
    public void paymentWaiveAllocation(TlWaivePaymentLog tlWaivePaymentLog) throws TxnBizException {
        //判断是否存在差错
        TlMistakeLog mistakeLog = extTlMistakeLogMapper.selectMistake(tlWaivePaymentLog.getSeqNo(), tlWaivePaymentLog.getCustomerId(),
                tlWaivePaymentLog.getTenantId(), TransBizConstant.MIS_STATUS_0);
        if (null != mistakeLog) {
            return;
        }

        Date bussinessDate = null;
        try {
            PrOrganizationTable organization =
                    organizationTableService.findByOrId(tlWaivePaymentLog.getOrganizationId(), tlWaivePaymentLog.getTenantId());
            bussinessDate = organization.getNextProcessingDate();
            this.dealWaiveAllocation(tlWaivePaymentLog, bussinessDate);
        } catch (Exception e) {
            //还款出现异常保存差错，后续进行差错处理
            logger.error("还款分配发生异常", e);
            mistakeLog = mistakeService.buildMistakeLog(tlWaivePaymentLog, bussinessDate, TransBizConstant.MIS_TYPE_3);
            extTlMistakeLogMapper.insertSelective(mistakeLog);
            return;
        }

        // mq发送贴息还款流水给出资方
        if (TransBizConstant.YES_FLAG.equals(tlWaivePaymentLog.getJointLoanFlag())) {
            try {
                this.sendMqPaymentLog(TransBizConstant.MQ_INVESTOR_WAIVE_PAYMENT_TOPIC + grayVersionConfig.getCurrentVersion(), tlWaivePaymentLog);
            } catch (Exception e) {
                logger.error("mq发送贴息还款流水给出资方异常:", e);
                mistakeLog = mistakeService.buildMistakeLog(tlWaivePaymentLog, bussinessDate, TransBizConstant.MIS_TYPE_6);
                extTlMistakeLogMapper.insertSelective(mistakeLog);
                return;
            }
        }

        logger.info("更新还款流水");
        //更新流水表状态
        TlWaivePaymentLog updateLog = new TlWaivePaymentLog();
        updateLog.setId(tlWaivePaymentLog.getId());
        updateLog.setCustomerId(tlWaivePaymentLog.getCustomerId());
        updateLog.setTenantId(tlWaivePaymentLog.getTenantId());
        updateLog.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_S);
        extTlWaivePaymentLogMapper.updateByIdAndCustomerIdAndTenantId(updateLog);

        logger.info("还款分配结束");
    }

    @Override
    public void dealWaiveAllocation(TlWaivePaymentLog tlWaivePaymentLog, Date bussinessDate) throws TxnException {
        logger.info("开始还款分配");
        List<TlPaymentAllocationLog> tlPaymentAllocationLogList = extTlPaymentAllocationLogMapper.findAllocationLogs(tlWaivePaymentLog.getSeqNo(), tlWaivePaymentLog.getCustomerId(), tlWaivePaymentLog.getTenantId());
        if (CollectionUtils.isNotEmpty(tlPaymentAllocationLogList)) {
            return;
        }

        //根据还款流水查询还款流水与订单映射信息
        List<TlWaivePaymentMapping> tlWaivePaymentMappingList =
                extTlWaivePaymentMappingMapper.selectByPaymentSeqAndCustomerId(tlWaivePaymentLog.getSeqNo(),
                        tlWaivePaymentLog.getCustomerId(), tlWaivePaymentLog.getTenantId());
        List<AmLoanMainInfo> loanList = new ArrayList<>();
        if (CollectionUtils.isEmpty(tlWaivePaymentMappingList)) {
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        //查询订单信息
        for (TlWaivePaymentMapping mapping : tlWaivePaymentMappingList) {
            logger.info("还款分配关联，mapping:{}", mapping);
            AmLoanMainInfo loanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndCusId(mapping.getLoanId(),
                    tlWaivePaymentLog.getCustomerId(), tlWaivePaymentLog.getTenantId());
            logger.info("订单，loanMainInfo:{}", loanMainInfo);
            loanList.add(loanMainInfo);
        }

        String paymentType = tlWaivePaymentLog.getPaymentType();
        if (TransBizConstant.REPAYMENT_TYPE_BILLPAY.equals(paymentType)
                || TransBizConstant.REPAYMENT_TYPE_WAIVEAUTO.equals(paymentType)) {
            //账单正常还款
            dealWaivePaymentService.dealWaiveBillPay(tlWaivePaymentLog, loanList, bussinessDate);
        } else if (TransBizConstant.REPAYMENT_TYPE_BILLPREPAYCURR.equals(paymentType)
                || TransBizConstant.REPAYMENT_TYPE_PREPAY.equals(paymentType)
        ) {
            //账单提前还当期还款
            dealWaivePaymentService.dealWaiveBillPrepayCurr(tlWaivePaymentLog, loanList, bussinessDate);
        } else if (TransBizConstant.REPAYMENT_TYPE_PRETERMINTION.equals(paymentType)
                || TransBizConstant.REPAYMENT_TYPE_STAGING.equals(paymentType)) {
            //账单提前还当期还款
            dealWaivePaymentService.dealWaiveBillPayOff(tlWaivePaymentLog, loanList, bussinessDate);
        }
    }

    /**
     * 校验提前还款及试算还款本金与还款总金额
     * 还款本金与还款总金额 必须且只能传一个值
     *
     * @param principalAmount
     * @param paymentAmount
     */
    private void validatePrePayAmt(BigDecimal principalAmount, BigDecimal paymentAmount) throws TxnBizException {
        if ((principalAmount != null && paymentAmount != null) || (principalAmount == null && paymentAmount == null)) {
            logger.error(TxnRespCode.PAYMENT_PRINCIPAL_AMOUNT_WRONG.getMsg());
            throw new TxnBizException(TxnRespCode.PAYMENT_PRINCIPAL_AMOUNT_WRONG.getCode(),
                    TxnRespCode.PAYMENT_PRINCIPAL_AMOUNT_WRONG.getUserTips());
        }
    }

    @Override
    public TxnRespResult<PaymentRes> handlePayment(HandlePaymentReq handlePaymentReq, String tenantId, String
            userId) throws TxnException {
        TxnRespResult<PaymentRes> respResult = new TxnRespResult();
        String paymentMethod = handlePaymentReq.getPaymentMethod();
        BigDecimal repayAmount = handlePaymentReq.getRepayAmount();
        CmCustomerSecondLevel cmCustomerSecondLevel = customerService.getSecondByChannelAndCustomerIdAndTensnId(handlePaymentReq.getChannel(), handlePaymentReq.getCustomerId(), tenantId, null);

        // 客户日终账务处理
        custBatchService.dealCustBatch(cmCustomerSecondLevel.getCustomerId(), cmCustomerSecondLevel.getOrganizationId(), cmCustomerSecondLevel.getTenantId());

        //获取差错订单号列表
        List<String> loanIdList = getMistakeLoanIdList(cmCustomerSecondLevel.getCustomerId(), tenantId);

        List<PaymentLoanInfoBean> loanList = new ArrayList();
        PaymentLoanInfoBean bean = new PaymentLoanInfoBean();
        bean.setLoanId(handlePaymentReq.getLoanId());
        loanList.add(bean);

        CustomerRes customer = customerService.getCustomerByChannelAndUserId(handlePaymentReq.getChannel(),
                cmCustomerSecondLevel.getUserId(), tenantId);

        PrOrganizationTable org = organizationTableService.findByOrId(TransBizConstant.DEFAULT_ORG_ID, tenantId);
        Date businessDate = org.getNextProcessingDate();

        //获取订单信息
        AmLoanMainInfo loanMainInfo = loanMainInfoService.findLoanMainInfoByLoanIdOrContractNumber(handlePaymentReq.getLoanId(), null, customer.getSecondLevel().getCustomerId(), tenantId, handlePaymentReq.getChannel());
        //获取订单账户
        AmLoanAccountInfo amLoanAccountInfo = loanAccountInfoService.getAmLoanAccountInfoByLoanIdAndCusId(handlePaymentReq.getLoanId(),
                handlePaymentReq.getCustomerId(), tenantId);
        //获取产品相关信息
        PrProductTable product = productTableService.findProByProIdAndOrgId(amLoanAccountInfo.getProductId(),
                org.getOrganizationId(), tenantId);


        if (TransBizConstant.REPAYMENT_TYPE_PREPAY.equals(paymentMethod)) {

            //还款校验
            validatePayment(loanMainInfo, amLoanAccountInfo, businessDate, loanIdList);
            //提前还款校验
            validatePrePay(amLoanAccountInfo, loanMainInfo, businessDate);

            PrePayTrialReq prePayTrialReq = new PrePayTrialReq();
            prePayTrialReq.setPrincipalAmount(handlePaymentReq.getRepayAmount());
            prePayTrialReq.setLoanId(handlePaymentReq.getLoanId());
            prePayTrialReq.setChannel(handlePaymentReq.getChannel());
            prePayTrialReq.setTenantId(tenantId);
            prePayTrialReq.setUserId(cmCustomerSecondLevel.getUserId());
            PrePayTrialRes res = prePayTrail(prePayTrialReq);
            handlePaymentReq.setRepayAmount(res.getTotalAmount());
        } else if (TransBizConstant.REPAYMENT_TYPE_PRETERMINTION.equals(paymentMethod)) {

            //验证订单是否允许提前还款
            validatePayment(loanMainInfo, amLoanAccountInfo, businessDate, loanIdList);
            //获取提前结清信息，判断是否允许提前结清
            getPrPreterminationFeeTableRes(loanMainInfo, amLoanAccountInfo);

            PaymentTrialReq paymentTrialReq = new PaymentTrialReq();
            paymentTrialReq.setLoanList(loanList);
            paymentTrialReq.setChannel(handlePaymentReq.getChannel());
            paymentTrialReq.setTenantId(tenantId);
            paymentTrialReq.setUserId(cmCustomerSecondLevel.getUserId());
            AdvanceSettleTrialRes res = preterminationTrail(paymentTrialReq);
            if (repayAmount.compareTo(res.getTotalAmount()) != 0) {
                logger.error("金额不满足还款条件");
                throw new TxnBizException(TxnRespCode.PAYMENT_AMOUNT_ZERO.getCode(), TxnRespCode.PAYMENT_AMOUNT_ZERO.getUserTips());
            }
        } else if (TransBizConstant.REPAYMENT_TYPE_OVERDUE.equals(paymentMethod)) {

            //验证订单是否允许提前还款
            validatePayment(loanMainInfo, amLoanAccountInfo, businessDate, loanIdList);

            //验证订单是否允许逾期还款
            validateOverPay(amLoanAccountInfo);

            DelinquencyTrialReq paymentTrialReq = new DelinquencyTrialReq();
            paymentTrialReq.setLoanList(loanList);
            paymentTrialReq.setChannel(handlePaymentReq.getChannel());
            paymentTrialReq.setTenantId(tenantId);
            paymentTrialReq.setUserId(cmCustomerSecondLevel.getUserId());
            DelinquencyTrialRes res = overdueTrail(paymentTrialReq);
            if (repayAmount.compareTo(res.getTotalAmount()) > 0) {
                logger.error("金额不满足还款条件");
                throw new TxnBizException(TxnRespCode.PAYMENT_AMOUNT_ZERO.getCode(), TxnRespCode.PAYMENT_AMOUNT_ZERO.getUserTips());
            }

        } else if (TransBizConstant.REPAYMENT_TYPE_PAY.equals(paymentMethod)) {

            //还款校验
            validatePayment(loanMainInfo, amLoanAccountInfo, businessDate, loanIdList);
            //提前还款校验
            validateOnDuePay(amLoanAccountInfo, businessDate);

            OnDuePayTrialReq onDuePayTrialReq = new OnDuePayTrialReq();
            onDuePayTrialReq.setLoanList(loanList);
            onDuePayTrialReq.setChannel(handlePaymentReq.getChannel());
            onDuePayTrialReq.setTenantId(tenantId);
            onDuePayTrialReq.setUserId(cmCustomerSecondLevel.getUserId());
            OnDuePayTrialRes res = onDuePayTrial(onDuePayTrialReq);
            if (repayAmount.compareTo(res.getTotalAmount()) != 0) {
                logger.error("金额不满足还款条件");
                throw new TxnBizException(TxnRespCode.PAYMENT_AMOUNT_ZERO.getCode(), TxnRespCode.PAYMENT_AMOUNT_ZERO.getUserTips());
            }
        } else if (TransBizConstant.REPAYMENT_TYPE_REFUND.equals(paymentMethod)) {
            RefundReq refundReq = new RefundReq();
            refundReq.setOrigSeqNo(loanMainInfo.getSerialNumber());
            refundReq.setRefundAmount(handlePaymentReq.getRepayAmount());
            refundReq.setSeqNo(SeqUtils.getSNByLength(20));
            refundReq.setUserId(cmCustomerSecondLevel.getUserId());
            refundReq.setChannel(loanMainInfo.getChannel());
            refundReq.setTenantId(tenantId);
            refundReq.setSubChannel(loanMainInfo.getSubChannel());
            refundReq.setThirdChannel(loanMainInfo.getThirdChannel());
            refundReq.setMerchantName(loanMainInfo.getMerchantName());
            try {
                this.refund(refundReq);
                respResult.getSuccess(new PaymentRes());
            } catch (Exception e) {
                e.printStackTrace();
                respResult.getFail(TxnRespCode.ERROR.getCode(), e);
            }
            return respResult;
        }

        CmCustomerBankcardInfo customerBank;
        //查询卡信息
        if ("1".equals(handlePaymentReq.getCardType())) {
            customerBank = bankCardService.getBankCardByCondition(handlePaymentReq.getCustomerId(), null,
                    handlePaymentReq.getCardNo(), handlePaymentReq.getChannel(), tenantId);
        } else {
            customerBank = new CmCustomerBankcardInfo();
            customerBank.setCardNumber(handlePaymentReq.getCardNo());
            SystemParameterRes bankIdRes = systemParameterService.findByParamCode(TransBizConstant.INNER_BANK_ID
                    , tenantId);
            SystemParameterRes bankNameRes = systemParameterService.findByParamCode(TransBizConstant.INNER_BANK_NANE
                    , tenantId);
            customerBank.setBankId(bankIdRes.getParamValue());
            customerBank.setBankName(bankNameRes.getParamValue());
            customerBank.setCardType(handlePaymentReq.getCardType());
            customerBank.setTenantId(tenantId);
        }


        TlPaymentLog paymentLog = paymentLogService.buildTlPaymentLog(handlePaymentReq.getPaymentMethod(),
                customerBank, customer, handlePaymentReq.getRepayAmount(), businessDate
                , product, null);
        paymentLog.setUpdateBy(userId);

        TlPaymentMapping paymentMapping = paymentLogService.buildTlPaymentMapping(handlePaymentReq.getCustomerId(),
                paymentLog.getSeqNo(), handlePaymentReq.getLoanId(), tenantId);


        //流水及流水订单映射入库
        tlPaymentLogMapper.insertSelective(paymentLog);
        tlPaymentMappingMapper.insertSelective(paymentMapping);


        respResult.getSuccess(new PaymentRes());
        return respResult;
    }

    /**
     * 手工账单还款-跳过扣款步骤
     *
     * @param handleSkipPayReq
     * @param tenantId
     * @return
     * @throws TxnException
     */
    @Override
    public TxnRespResult<HandleSkipPayRes> handleBillSkipPay(HandleSkipPayReq handleSkipPayReq, String tenantId) throws TxnException {
        TxnRespResult<HandleSkipPayRes> respResult = new TxnRespResult();
        respResult.getSuccess(new HandleSkipPayRes());
        PrOrganizationTable org = organizationTableService.findByOrId(TransBizConstant.DEFAULT_ORG_ID, tenantId);
        Date businessDate = org.getNextProcessingDate();
        String channel = handleSkipPayReq.getChannel();
        String customerId = handleSkipPayReq.getCustomerId();
        CmCustomerSecondLevel cmCustomerSecondLevel = customerService.getSecondByChannelAndCustomerIdAndTensnId(channel, customerId, tenantId, null);
        CustomerRes customer = customerService.getCustomerByChannelAndUserId(channel, cmCustomerSecondLevel.getUserId(), tenantId);

        // 客户日终账务处理
        custBatchService.dealCustBatch(customerId, cmCustomerSecondLevel.getOrganizationId(), cmCustomerSecondLevel.getTenantId());

        List<String> statusList = Arrays.asList(TransBizConstant.LOAN_STATUS_1, TransBizConstant.LOAN_STATUS_2);
        List<String> productIds = null;
        if (StringUtils.isNotBlank(handleSkipPayReq.getBizType())) {
            productIds = BizTypeProducts.getByBizType(BizTypeEnum.getByCode(handleSkipPayReq.getBizType()));
        }
        if (CollectionUtils.isEmpty(productIds)) {
            respResult.getFail(TxnRespCode.ERROR.getCode(), "产品类型不存在");
            return respResult;
        }
        //查询客户正常状态贷款账户列表
        List<AmLoanAccountInfo> accountInfos = extAmLoanAccountInfoMapper.selectByCustAndChannelAndStatusAndProductId(customerId, channel, tenantId, statusList, productIds);
        //订单列表为空
        if (CollectionUtils.isEmpty(accountInfos)) {
            respResult.getFail(TxnRespCode.ERROR.getCode(), "订单列表为空");
            return respResult;
        }
        //筛选符合条件的订单 排除掉核销的订单
        Predicate<AmLoanAccountInfo> getOnDue =
                loan -> TransBizConstant.LOAN_STATUS_1.equals(loan.getAccountStatus()) && loan.getNextPaymentDate().compareTo(businessDate) == 0 && !loan.getChargeoffFlag().equals(TransBizConstant.YES_FLAG);
        Predicate<AmLoanAccountInfo> getOverDue = loan -> TransBizConstant.LOAN_STATUS_2.equals(loan.getAccountStatus()) && !loan.getChargeoffFlag().equals(TransBizConstant.YES_FLAG);
        List<AmLoanAccountInfo> newLoanAcctList = accountInfos.stream().filter(getOnDue.or(getOverDue)).collect(Collectors.toList());

        //若验证后的订单列表不为空
        if (CollectionUtils.isEmpty(newLoanAcctList)) {
            respResult.getFail(TxnRespCode.ERROR.getCode(), "订单列表为空");
            return respResult;
        }

        //查询客戶下处理中的还款流水
        String[] sumStatusList = {TransBizConstant.PAYMENT_LOG_SUM_STATUS_O, TransBizConstant.PAYMENT_LOG_SUM_STATUS_T, TransBizConstant.PAYMENT_LOG_SUM_STATUS_D};
        List<TlPaymentLog> tlPaymentLogList = extTlPaymentLogMapper.selectByCustomerIdProductsAndSumStatus(customerId, sumStatusList, tenantId, productIds);
        //还款流水不空 与订单匹配
        if (CollectionUtils.isNotEmpty(tlPaymentLogList)) {
            List<String> seqNoList = tlPaymentLogList.stream().map(TlPaymentLog::getSeqNo).collect(Collectors.toList());
            //获取还款流水对应的订单信息
            List<String> loanIdList = extTlPaymentMappingMapper.selectBySeqNoListForBatch(seqNoList);
            //剔除正在还款中的数据
            newLoanAcctList.removeIf(amLoanAccountInfo -> loanIdList.contains(amLoanAccountInfo.getLoanId()));
        }
        //若验证后的订单列表不为空
        if (CollectionUtils.isEmpty(newLoanAcctList)) {
            respResult.getFail(TxnRespCode.ERROR.getCode(), "订单列表为空");
            return respResult;
        }
        //查询产品信息
        PrProductTable product = productTableService.findProByProIdAndOrgId(newLoanAcctList.get(0).getProductId(), org.getOrganizationId(), tenantId);
        //查询客户是否有默认还款卡
        CmCustomerBankcardInfo customerBank = bankCardService.getBankCardByCondition(customerId, BankCardConstant.DEFAULT_FLAG_Y, null, channel, tenantId);
        //构建还款流水
        TlPaymentLog tlPaymentLog = paymentLogService.buildTlPaymentLog(TransBizConstant.REPAYMENT_TYPE_AUTO, customerBank, customer, BigDecimal.ZERO, businessDate, product, null);
        tlPaymentLog.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_T);
        tlPaymentLog.setReturnStatus("S");
        tlPaymentLog.setReturnCode("0000");
        tlPaymentLog.setReturnMsg("构造还款成功");
        //还款流水与订单映射信息
        List<TlPaymentMapping> tlPaymentMappingList = new ArrayList<>(newLoanAcctList.size());
        String paymentType = "";
        // 逾期次数
        int overCount = 0;
        //总欠款
        BigDecimal totalDebt = BigDecimal.ZERO;
        //遍历订单计算欠款
        for (AmLoanAccountInfo amLoanAccountInfo : newLoanAcctList) {
            //计算欠款
            DebtBean debtBean = debtCalculationService.getBalance(amLoanAccountInfo, businessDate, null, true, true, null);
            //没有欠款 跳过
            if (debtBean.getTotalAmount().compareTo(BigDecimal.ZERO) < 1) {
                continue;
            }
            totalDebt = totalDebt.add(debtBean.getTotalAmount());
            //构建还款流水与订单映射
            TlPaymentMapping tlPaymentMapping = paymentLogService.buildTlPaymentMapping(customerId, tlPaymentLog.getSeqNo(), amLoanAccountInfo.getLoanId(), tenantId);
            tlPaymentMappingList.add(tlPaymentMapping);
            if (amLoanAccountInfo.getAccountStatus().equalsIgnoreCase(TransBizConstant.LOAN_STATUS_2)) {
                overCount++;
            }
        }
        if (totalDebt.compareTo(BigDecimal.ZERO) <= 0) {//若存在欠款 返回信息
            respResult.getFail(TxnRespCode.ERROR.getCode(), "用户无欠款");
            return respResult;
        }
        String productId = productIds.get(0);
        // 是否有指定的还款金额
        BigDecimal skipAmount = handleSkipPayReq.getSkipAmount();
        if (totalDebt.compareTo(skipAmount) < 0) {
            respResult.getFail(TxnRespCode.ERROR.getCode(), "还款金额不能比总欠款金额大");
            return respResult;
        }
        tlPaymentLog.setAmount(Objects.nonNull(skipAmount) && BigDecimal.ZERO.compareTo(skipAmount) < 0 ? skipAmount : totalDebt);
        //2 获取查询月份账单日对应的还款日
        Integer paymentDd = customer.getSecondLevel().getPaymentDd();
        Integer statementDd = customer.getSecondLevel().getStatementDd();
        statementDd = this.getStatementDayByProductId(businessDate, statementDd, productId);
        //获取当期及下期账单日
        CurrentStatementBean bean = loanHandlerImpl.getCurrentStatement(productId, statementDd, paymentDd, businessDate);
        Date paymentDate = bean.getPaymentDate();
        if (overCount > 0) {
            paymentType = TransBizConstant.REPAYMENT_TYPE_BILLOVERDUEPAY;
        } else {
            //账单正常还款
            if (businessDate.compareTo(paymentDate) == 0) {
                paymentType = TransBizConstant.REPAYMENT_TYPE_BILLPAY;
            } else {
                //账单提前还当期
                paymentType = TransBizConstant.REPAYMENT_TYPE_BILLPREPAYCURR;
            }
        }
        tlPaymentLog.setPaymentType(paymentType);
        //流水及流水订单映射入库
        tlPaymentLogMapper.insertSelective(tlPaymentLog);
        extTlPaymentMappingMapper.batchInsert(tlPaymentMappingList);
        return respResult;
    }

    /**
     * 手工账单还款-跳过扣款步骤
     *
     * @param handleSkipPayReq
     * @param tenantId
     * @return
     * @throws TxnException
     */
    @Override
    public TxnRespResult<HandleSkipPayRes> handlePreTerminationSkipPay(HandleSkipPayReq handleSkipPayReq, String tenantId) throws TxnException {
        TxnRespResult<HandleSkipPayRes> respResult = new TxnRespResult();
        respResult.getSuccess(new HandleSkipPayRes());
        PrOrganizationTable org = organizationTableService.findByOrId(TransBizConstant.DEFAULT_ORG_ID, tenantId);
        Date businessDate = org.getNextProcessingDate();
        String channel = handleSkipPayReq.getChannel();
        String customerId = handleSkipPayReq.getCustomerId();
        CmCustomerSecondLevel cmCustomerSecondLevel = customerService.getSecondByChannelAndCustomerIdAndTensnId(channel, customerId, tenantId, null);
        CustomerRes customer = customerService.getCustomerByChannelAndUserId(channel, cmCustomerSecondLevel.getUserId(), tenantId);

        List<String> productIds = null;
        if (StringUtils.isNotBlank(handleSkipPayReq.getBizType())) {
            productIds = BizTypeProducts.getByBizType(BizTypeEnum.getByCode(handleSkipPayReq.getBizType()));
        }
        if (CollectionUtils.isEmpty(productIds)) {
            respResult.getFail(TxnRespCode.ERROR.getCode(), "产品类型不存在");
            return respResult;
        }
        List<AmLoanAccountInfo> amLoanAccountInfoList = extAmLoanAccountInfoMapper.selectByCustAndChannelAndStatus(customerId, channel, tenantId, Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1,
                TransBizConstant.ACCOUNT_STATUS_2));
        //订单列表为空
        if (CollectionUtils.isEmpty(amLoanAccountInfoList)) {
            respResult.getFail(TxnRespCode.ERROR.getCode(), "订单列表为空");
            return respResult;
        }
        //查询客戶下处理中的还款流水
        String[] sumStatusList = {TransBizConstant.PAYMENT_LOG_SUM_STATUS_O, TransBizConstant.PAYMENT_LOG_SUM_STATUS_T, TransBizConstant.PAYMENT_LOG_SUM_STATUS_D};
        List<TlPaymentLog> tlPaymentLogList = extTlPaymentLogMapper.selectByCustomerIdProductsAndSumStatus(customerId, sumStatusList, tenantId, productIds);
        //还款流水不空
        if (CollectionUtils.isNotEmpty(tlPaymentLogList)) {
            respResult.getFail(TxnRespCode.ERROR.getCode(), "该用户有还款正在进行中，请稍后再试");
            return respResult;
        }
        String productId = productIds.get(0);
        //查询产品信息
        PrProductTable product = productTableService.findProByProIdAndOrgId(productId, org.getOrganizationId(), tenantId);
        //查询客户是否有默认还款卡
        CmCustomerBankcardInfo customerBank = bankCardService.getBankCardByCondition(customerId, BankCardConstant.DEFAULT_FLAG_Y, null, channel, tenantId);
        //构建还款流水
        TlPaymentLog tlPaymentLog = paymentLogService.buildTlPaymentLog(TransBizConstant.REPAYMENT_TYPE_PRETERMINTION, customerBank, customer, BigDecimal.ZERO, businessDate, product, null);
        tlPaymentLog.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_T);
        tlPaymentLog.setReturnStatus("S");
        tlPaymentLog.setReturnCode("0000");
        tlPaymentLog.setReturnMsg("构造还款成功");
        //还款流水与订单映射信息
        List<TlPaymentMapping> tlPaymentMappingList = new ArrayList<>(amLoanAccountInfoList.size());
        List<LoanInformationRes> list = iCustomerOrderService.getLoanInformation(channel, customerId, tenantId);
        //总欠款
        BigDecimal totalDebt = list.get(0).getTotalAmount();
        if (totalDebt.compareTo(BigDecimal.ZERO) <= 0) {//若存在欠款 返回信息
            respResult.getFail(TxnRespCode.ERROR.getCode(), "用户无欠款");
            return respResult;
        }
        tlPaymentLog.setAmount(totalDebt);
        Integer paymentDd = customer.getSecondLevel().getPaymentDd();
        Integer statementDd = customer.getSecondLevel().getStatementDd();
        statementDd = this.getStatementDayByProductId(businessDate, statementDd, productId);
        //获取当期及下期账单日
        CurrentStatementBean bean = loanHandlerImpl.getCurrentStatement(productId, statementDd, paymentDd, businessDate);
        Date paymentDate = bean.getPaymentDate();
        //构建还款流水与订单映射
        for (AmLoanAccountInfo amLoanAccountInfo : amLoanAccountInfoList) {
            if (TransBizConstant.ACCOUNT_STATUS_2.equalsIgnoreCase(amLoanAccountInfo.getAccountStatus()) || amLoanAccountInfo.getNextPaymentDate().compareTo(paymentDate) <= 0) {
                respResult.getFail(TxnRespCode.ERROR.getCode(), "当期账单未还完，不允许提前结清");
                return respResult;
            }
            TlPaymentMapping tlPaymentMapping = paymentLogService.buildTlPaymentMapping(customerId, tlPaymentLog.getSeqNo(), amLoanAccountInfo.getLoanId(), tenantId);
            tlPaymentMappingList.add(tlPaymentMapping);
        }
        //流水及流水订单映射入库
        tlPaymentLogMapper.insertSelective(tlPaymentLog);
        extTlPaymentMappingMapper.batchInsert(tlPaymentMappingList);
        return respResult;
    }


    @Override
    public ThirdPartyPaymentRes thirdPartyPayment(ThirdPartyPaymentRep thirdPartyPaymentRep) throws TxnException {
        ThirdPartyPaymentRes res = BeanMapping.copy(thirdPartyPaymentRep, ThirdPartyPaymentRes.class);
        //客户信息
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(thirdPartyPaymentRep.getUserId(), thirdPartyPaymentRep.getChannel(),
                thirdPartyPaymentRep.getTenantId(), null);
        if (null == customer) {
            logger.error("客户信息未找到，用户编号：{}，渠道：{}", thirdPartyPaymentRep.getUserId(), thirdPartyPaymentRep.getChannel());
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }
        //客户跑批改造
        custBatchService.dealCustBatch(customer.getCustomerId(), customer.getOrganizationId(), customer.getTenantId());
        String customerId = customer.getCustomerId();
        String channel = customer.getChannel();
        String orgId = customer.getOrganizationId();

        //业务日期
        PrOrganizationTable org = organizationTableService.findByOrId(orgId,
                customer.getTenantId());
        Date busiDate = org.getNextProcessingDate();

        //校验请求参数
        checkThirdPartyPayment(thirdPartyPaymentRep, busiDate);

        //校验客户是否重复发起还款
        int count = extTlThirdPartyPaymentMapper.selectPaymentStatusByCustomerIdAndSumStatus(
                TransBizConstant.THIRD_PARTY_STATUS_T, customerId, customer.getTenantId());
        if (count > 0) {
            logger.error("订单->{}当前还在还款中，不允许还款", thirdPartyPaymentRep.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_PROCESSING_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_PROCESSING_NOT_ALLOW.getUserTips());
        }

        //获取差错订单号列表
        List<String> loanIdList = getMistakeLoanIdList(customerId, customer.getTenantId());

        //幂等性校验
        TlThirdPartyPayment log = extTlThirdPartyPaymentMapper.selectByPayId(thirdPartyPaymentRep.getSeqNo(), customerId, thirdPartyPaymentRep.getTenantId());
        if (null != log) {
            buildTlThirdPartyPaymentRes(thirdPartyPaymentRep, log, res);
            return res;
        }

        //获取订单账户
        AmLoanMainInfo loanMainInfo = loanMainInfoService.findLoanMainInfoByLoanIdOrContractNumber(thirdPartyPaymentRep.getLoanId(),
                thirdPartyPaymentRep.getContractNumber(), customerId, thirdPartyPaymentRep.getTenantId(), thirdPartyPaymentRep.getChannel());
        AmLoanAccountInfo loanAccountInfo = loanAccountMainInfoService.getAmLoanAccountInfoByLoanIdAndCusId(thirdPartyPaymentRep.getLoanId(),
                customerId, thirdPartyPaymentRep.getTenantId());
        loanService.checkLoanStatus(loanAccountInfo);

        //通用还款校验
        validateBillPay(loanMainInfo, loanAccountInfo, loanIdList);

        //新增按照不同还款方式校验是否允许还款
        validateByPaymentType(thirdPartyPaymentRep, loanMainInfo, loanAccountInfo, busiDate);
        logger.debug("计算欠款并生成第三方还款流水");
        TlThirdPartyPayment thirdPartyPayment = thirdPartyPaymentService.thirdPartyPay(thirdPartyPaymentRep, busiDate, loanMainInfo, loanAccountInfo);
        buildTlThirdPartyPaymentRes(thirdPartyPaymentRep, thirdPartyPayment, res);
        return res;

    }

    @Override
    public void cmpsCrtAllocation(TlPaymentLog tlPaymentLog) throws TxnBizException {
        //判断是否存在差错
        TlMistakeLog mistakeLog = extTlMistakeLogMapper.selectMistake(tlPaymentLog.getSeqNo(), tlPaymentLog.getCustomerId(),
                tlPaymentLog.getTenantId(), TransBizConstant.MIS_STATUS_0);
        if (null != mistakeLog) {
            return;
        }

        Date bussinessDate = null;
        try {
            PrOrganizationTable organization =
                    organizationTableService.findByOrId(tlPaymentLog.getOrganizationId(), tlPaymentLog.getTenantId());
            bussinessDate = organization.getNextProcessingDate();
            this.dealAllocation(tlPaymentLog, bussinessDate);
        } catch (Exception e) {
            //还款出现异常保存差错，后续进行差错处理
            logger.error("代偿回购还款分配发生异常", e);
            mistakeLog = mistakeService.buildMistakeLog(tlPaymentLog, bussinessDate, TransBizConstant.MIS_TYPE_1);
            extTlMistakeLogMapper.insertSelective(mistakeLog);
            return;
        }

        // mq发送还款流水给出资方
        try {
            this.sendMqPaymentLog(TransBizConstant.MQ_INVESTOR_PAYMENT_TOPIC + grayVersionConfig.getCurrentVersion(), tlPaymentLog);
        } catch (Exception e) {
            logger.error("mq发送还款流水给出资方异常：{}", e);
            mistakeLog = mistakeService.buildMistakeLog(tlPaymentLog, bussinessDate, TransBizConstant.MIS_TYPE_5);
            extTlMistakeLogMapper.insertSelective(mistakeLog);
            return;
        }

        logger.info("更新还款流水");
        //更新流水表状态
        TlPaymentLog updateLog = new TlPaymentLog();
        updateLog.setId(tlPaymentLog.getId());
        updateLog.setCustomerId(tlPaymentLog.getCustomerId());
        updateLog.setTenantId(tlPaymentLog.getTenantId());
        updateLog.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_S);
        extTlPaymentLogMapper.updateByIdAndCustomerIdAndTenantId(updateLog);

        //短信处理 需要根据sum_status 获取对应的模板
        tlPaymentLog.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_S);
        try {
            iSmsService.paymentProcess(tlPaymentLog);
        } catch (Exception e) {
            logger.error("代偿回购还款分配，短信发送异常", e);
        }

        logger.info("代偿回购还款分配结束");
    }

    @Override
    public ThirdPartyPaymentQueryRes thirdPartyPaymentQuery(ThirdPartyPaymentQueryReq request) throws TxnException {
        ThirdPartyPaymentQueryRes res = new ThirdPartyPaymentQueryRes();
        res.setUserId(request.getUserId());
        res.setPaymentSeqNo(request.getPaymentSeqNo());

        //客户信息
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(request.getUserId(), request.getChannel(),
                request.getTenantId(), null);
        if (null == customer) {
            logger.error("客户信息未找到，用户编号：{}，渠道：{}", request.getUserId(), request.getChannel());
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
        }

        TlThirdPartyPayment tlThirdPartyPayment = extTlThirdPartyPaymentMapper.selectPaymentStatusBySeqNo(request.getPaymentSeqNo(), customer.getCustomerId(), request.getTenantId());
        if (null == tlThirdPartyPayment) {
            logger.error("未获取到三方还款流水信息，流水号：{}", request.getPaymentSeqNo());
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(),
                    TxnRespCode.NO_DATA.getUserTips());
        }

        String sumStatus = tlThirdPartyPayment.getSumStatus();
        if (TransBizConstant.PAYMENT_LOG_SUM_STATUS_T.equals(sumStatus)) {
            sumStatus = TransBizConstant.THIRD_PARTY_STATUS_P;
        }
        res.setSumStatus(sumStatus);
        res.setResultDescription(tlThirdPartyPayment.getDescription());
        res.setLoanId(tlThirdPartyPayment.getLoanId());

        AmLoanMainInfo loanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndCusId(tlThirdPartyPayment.getLoanId(), customer.getCustomerId(), tlThirdPartyPayment.getTenantId());
        res.setContractNumber(loanMainInfo.getContractNumber());
        String paymentDate = DateUtils.format(tlThirdPartyPayment.getBusinessDate());
        res.setPaymentDate(paymentDate);
        res.setTotalAmount(tlThirdPartyPayment.getAmount());
        res.setPrincipalAmount(tlThirdPartyPayment.getPrincipalAmount());
        res.setInterestAmount(tlThirdPartyPayment.getInterestAmount());
        res.setPenaltyAmount(tlThirdPartyPayment.getPenaltyAmount());
        res.setCompoundAmount(tlThirdPartyPayment.getCompoundAmount());
        res.setFeeAmount(tlThirdPartyPayment.getFeeAmount());

        return res;
    }

    private void checkThirdPartyPayment(ThirdPartyPaymentRep rep, Date busiDate) throws TxnBizException {
        //校验还款方式
        List<String> paymentTypeList = Arrays.asList(TransBizConstant.REPAYMENT_TYPE_PAY, TransBizConstant.REPAYMENT_TYPE_PRETERMINTION,
                TransBizConstant.REPAYMENT_TYPE_OVERDUE, TransBizConstant.REPAYMENT_TYPE_PAY_TERM, TransBizConstant.REPAYMENT_TYPE_CMPS, TransBizConstant.REPAYMENT_TYPE_CRT);
        if (!paymentTypeList.contains(rep.getPaymentType())) {
            logger.error(TxnRespCode.PAYMENT_PAYMENT_TYPE_NOT_ALLOW.getMsg());
            throw new TxnBizException(TxnRespCode.PAYMENT_PAYMENT_TYPE_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_PAYMENT_TYPE_NOT_ALLOW.getUserTips());
        }

        //校验还款期次
        if (TransBizConstant.REPAYMENT_TYPE_PAY.equals(rep.getPaymentType()) ||
                TransBizConstant.REPAYMENT_TYPE_CMPS.equals(rep.getPaymentType()) ||
                TransBizConstant.REPAYMENT_TYPE_PAY_TERM.equals(rep.getPaymentType())) {
            if (null == rep.getPaymentTerm() || rep.getPaymentTerm() <= 0) {
                logger.error(TxnRespCode.PAYMENT_TERM_NO_DATA.getMsg());
                throw new TxnBizException(TxnRespCode.PAYMENT_TERM_NO_DATA.getCode(),
                        TxnRespCode.PAYMENT_TERM_NO_DATA.getUserTips());
            }
        }

        //合同号与外部合同号校验
        if (StringUtils.isBlank(rep.getLoanId()) && StringUtils.isBlank(rep.getContractNumber())) {
            logger.error(TxnRespCode.PAYMENT_LOAN_NO_DATA.getMsg());
            throw new TxnBizException(TxnRespCode.PAYMENT_LOAN_NO_DATA.getCode(),
                    TxnRespCode.PAYMENT_LOAN_NO_DATA.getUserTips());
        }

        //验证是否在批扣中
        validateAutoPayment(rep.getTenantId(), busiDate);
    }

    /**
     * 构建返回信息
     *
     * @param thirdPartyPaymentRep
     * @param log
     * @param res
     */
    private void buildTlThirdPartyPaymentRes(ThirdPartyPaymentRep thirdPartyPaymentRep, TlThirdPartyPayment
            log, ThirdPartyPaymentRes res) {
        res.setPaymentSeqNo(log.getSeqNo());
        if (TransBizConstant.PAYMENT_LOG_SUM_STATUS_T.equals(log.getSumStatus())) {
            res.setSumStatus(TransBizConstant.THIRD_PARTY_STATUS_P);
        } else {
            res.setSumStatus(log.getSumStatus());
        }
        res.setResultDescription(log.getDescription());
        res.setLoanId(log.getLoanId());
        res.setContractNumber(thirdPartyPaymentRep.getContractNumber());
        String paymentDate = DateUtils.format(log.getBusinessDate(), "yyyy-MM-dd");
        res.setPaymentDate(paymentDate);
        res.setTotalAmount(log.getAmount());
        res.setPrincipalAmount(log.getPrincipalAmount());
        res.setInterestAmount(log.getInterestAmount());
        res.setPenaltyAmount(log.getPenaltyAmount());
        res.setCompoundAmount(log.getCompoundAmount());
        res.setFeeAmount(log.getFeeAmount());
    }

    /**
     * 按照还款类型校验是否允许还款
     *
     * @param thirdPartyPaymentRep
     */
    private void validateByPaymentType(ThirdPartyPaymentRep thirdPartyPaymentRep, AmLoanMainInfo
            loanMainInfo, AmLoanAccountInfo loanAccountInfo, Date busiDate) throws TxnBizException {
        String paymentType = thirdPartyPaymentRep.getPaymentType();
        switch (paymentType) {
            case TransBizConstant.REPAYMENT_TYPE_PRETERMINTION://提前结清
                getPrPreterminationFeeTableRes(loanMainInfo, loanAccountInfo);
                break;
            case TransBizConstant.REPAYMENT_TYPE_PREPAY_CURR://提前还当期
                validatePrePay(loanAccountInfo, loanMainInfo, busiDate);
                break;
            case TransBizConstant.REPAYMENT_TYPE_OVERDUE://逾期还款
                validateOverPay(loanAccountInfo);
                break;
            case TransBizConstant.REPAYMENT_TYPE_PAY://正常还款
                validateOnDuePay(loanAccountInfo, busiDate);
                break;
            case TransBizConstant.REPAYMENT_TYPE_PAY_TERM://指定期次还款、
                validateOnPayTerm(thirdPartyPaymentRep, loanAccountInfo, busiDate);
                break;
        }

    }

    /**
     * 指定期次还款校验
     *
     * @param loanAccountInfo
     * @param busiDate
     */
    private void validateOnPayTerm(ThirdPartyPaymentRep thirdPartyPaymentRep, AmLoanAccountInfo
            loanAccountInfo, Date busiDate) throws TxnBizException {
        //判断指定期次是否已经到期
        AmRepaymentPlanInfo amRepaymentPlanInfo = extAmRepaymentPlanInfoMapper.selectByLoanIdAndTenor(loanAccountInfo.getCustomerId(), loanAccountInfo.getTenantId(),
                loanAccountInfo.getChannel(), loanAccountInfo.getLoanId(), thirdPartyPaymentRep.getPaymentTerm());
        if (busiDate.compareTo(amRepaymentPlanInfo.getPaymentDueDate()) < 0) {
            logger.error("订单->{}订单该期未到期不允许还款", loanAccountInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_LOAN_STATUS_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_LOAN_STATUS_NOT_ALLOW.getUserTips());
        }

        //判断指定期次之前是否有逾期
        int countNumber = extAmRepaymentPlanInfoMapper.selectByLoanIdAndBillingTenorAndPlanStatusList(
                loanAccountInfo.getLoanId(), loanAccountInfo.getCustomerId(), loanAccountInfo.getTenantId(), thirdPartyPaymentRep.getPaymentTerm(),
                Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2));
        if (countNumber > 0) {
            logger.error("订单->{}订单之前的期次未结清", loanAccountInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_LOAN_NO_CLEAN.getCode(),
                    TxnRespCode.PAYMENT_LOAN_NO_CLEAN.getUserTips());
        }
    }

    /**
     * 验证是否可以还款
     *
     * @param amLoanMainInfo 订单信息
     * @author : zhenghaopu
     * date: 2019/10/8
     */
    private void validateLoanStaging(AmLoanMainInfo amLoanMainInfo, AmLoanAccountInfo amLoanAccountInfo,
                                     List<String> loanIdList) throws TxnBizException {

        PrProductTable product = productTableService.findProByProIdAndOrgId(amLoanMainInfo.getProductId(),
                amLoanMainInfo.getOrganizationId(), amLoanMainInfo.getTenantId());

        //订单状态是否有逾期 正常外其他状态
        if (!amLoanAccountInfo.getAccountStatus().equals(TransBizConstant.LOAN_STATUS_1)) {
            logger.error("订单->{}当前状态不允许还款", amLoanMainInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_LOAN_STATUS_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_LOAN_STATUS_NOT_ALLOW.getUserTips());
        }

        if (TransBizConstant.YES_FLAG.equals(product.getRemitWhenLoan())) {
            //订单是否还未放款
            boolean disFlag = disbursementLogStatus(amLoanMainInfo.getLoanId(), amLoanMainInfo.getCustomerId(),
                    amLoanMainInfo.getTenantId());
            //未放款
            if (disFlag) {
                logger.error("订单在放款处理中，不允许还款");
                throw new TxnBizException(TxnRespCode.PAYMENT_LOANING_NOT_ALLOW.getCode(),
                        TxnRespCode.PAYMENT_LOANING_NOT_ALLOW.getUserTips());
            }
        }
        //订单有是否有还款中信息
        boolean paymentFlag = repaymentLogStatusFlag(amLoanMainInfo.getLoanId(), amLoanMainInfo.getCustomerId(),
                amLoanMainInfo.getTenantId()) || repaymentWaiveLogStatusFlag(amLoanMainInfo.getLoanId(), amLoanMainInfo.getCustomerId(),
                amLoanMainInfo.getTenantId());
        if (paymentFlag) {
            logger.error("订单->{}当前还在还款中，不允许还款", amLoanMainInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_PROCESSING_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_PROCESSING_NOT_ALLOW.getUserTips());
        }
        //判断是否有差错 有差错则报错
        if (!loanIdList.isEmpty() && loanIdList.contains(amLoanMainInfo.getLoanId())) {
            logger.error("订单->{}有差错未处理", amLoanMainInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_MISTAKE_EXIST.getCode(),
                    TxnRespCode.PAYMENT_MISTAKE_EXIST.getUserTips());
        }

    }

    /**
     * mq发送还款流水
     *
     * @param topic
     * @param data
     */
    @Override
    public void sendMqPaymentLog(String topic, Object data) throws Exception {
        //发送MQ消息
        String jsonMessage = JsonUtils.toJSon(data);
        logger.info("开始发送MQ消息，还款流水：{}", jsonMessage);
        SendResult sendResult = rocketMQTemplate.syncSend(topic, jsonMessage);
        logger.info("还款处理完成,发送结果：{}", sendResult);
    }

    @Override
    public PaymentAccessCheckRes paymentAccessCheck(PaymentAccessCheckReq paymentAccessCheckReq) throws
            TxnBizException {
        PaymentAccessCheckRes paymentAccessCheckRes = new PaymentAccessCheckRes();
        //获取客户信息
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(paymentAccessCheckReq.getUserId(), paymentAccessCheckReq.getChannel(),
                paymentAccessCheckReq.getTenantId(), null);

        PrOrganizationTable org = organizationTableService.findByOrId(TransBizConstant.DEFAULT_ORG_ID, paymentAccessCheckReq.getTenantId());
        Date businessDate = org.getNextProcessingDate();

        //获取差错订单号列表
        List<String> loanIdList = getMistakeLoanIdList(customer.getCustomerId(), customer.getTenantId());

        for (PaymentLoanInfoBean paymentLoanInfoBean : paymentAccessCheckReq.getLoanList()) {
            //获取订单信息
            AmLoanMainInfo loanMainInfo = getLoanMainInfo(paymentAccessCheckReq.getTenantId(),
                    customer.getCustomerId(), paymentLoanInfoBean.getLoanId(), paymentLoanInfoBean.getContractNumber(), paymentAccessCheckReq.getChannel());
            //获取订单账户信息
            AmLoanAccountInfo amLoanAccountInfo = loanAccountInfoService.getAmLoanAccountInfoByLoanIdAndCusId(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId());
            logger.debug("合同号->{}", loanMainInfo.getContractNumber());
            validatePayment(loanMainInfo, amLoanAccountInfo, businessDate, loanIdList);
        }

        //验证是否在批扣中
        Integer count = extBtAutoPaymentListMapper.selectByChannelAndCustomer(customer.getCustomerId(), paymentAccessCheckReq.getChannel(), paymentAccessCheckReq.getTenantId());
        if (count > 0) {
            validateAutoPayment(paymentAccessCheckReq.getTenantId(), org.getNextProcessingDate());
        }

        return paymentAccessCheckRes;

    }

    /**
     * 退款处理
     *
     * @param refundReq
     * @return
     * @throws TxnBizException
     */
    @Override
    public RefundRes refund(RefundReq refundReq) throws Exception {
        RefundRes refundRes = new RefundRes();

        //获取客户信息
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(refundReq.getUserId(), refundReq.getChannel(),
                refundReq.getTenantId(), null);

        //获取原订单
        AmLoanMainInfo loanMainInfo = extAmLoanMainInfoMapper.selectAmLoanMainInfoBySeq(refundReq.getOrigSeqNo(), customer.getCustomerId(),
                refundReq.getTenantId());
        if (null == loanMainInfo) {
            logger.error("原订单信息不存在");
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getMsg());
        }
        refundRes.setLoanId(loanMainInfo.getLoanId());
        refundRes.setLoanAmount(loanMainInfo.getLoanAmount());
        refundRes.setUserId(customer.getUserId());

        //幂等性校验
        TlTransactionInfo tlTransactionInfo = extTlTransactionInfoMapper.selectBySeqNo(refundReq.getSeqNo(), loanMainInfo.getCustomerId(), refundReq.getTenantId());
        if (Objects.nonNull(tlTransactionInfo)) {
            refundRes.setRefundAmount(tlTransactionInfo.getPostingAmount());
            return refundRes;
        }


        CmCustomerBankcardInfo cmCustomerBankcardInfo = extCmCustomerBankcardInfoMapper.selectBankCardByCondition(loanMainInfo.getCustomerId(), TransBizConstant.Y_FLAG,
                null, refundReq.getChannel(), refundReq.getTenantId());
        if (Objects.isNull(cmCustomerBankcardInfo) && Constant.C72.equalsIgnoreCase(refundReq.getThirdChannel())) {
            refundReq.setThirdChannel(Constant.THIRD_CHANNEL);
        }
        //校验请求报文以及获取机构 客户  银行 订单 等信息
        RepaymentParamBean repaymentParamBean = commonRepayment(refundReq, null);
        if (Objects.isNull(repaymentParamBean.getBank())) {
            String extFields = loanMainInfo.getExtFields();
            JSONObject jsonObject = JSONObject.parseObject(extFields);
            //  收款人银行账号
            Object recverBankAcctNo = jsonObject.get("RECVER_BANK_ACCT_NO");
            // 收款人银行行号
            Object recverBankBankNo = jsonObject.get("RECVER_BANK_BANK_NO");
            // 收款人银行全称
            Object recverBankFullname = jsonObject.get("RECVER_BANK_FULLNAME");
            CmCustomerBankcardInfo bank = new CmCustomerBankcardInfo();
            bank.setBankId(getString(recverBankBankNo));
            bank.setBankName(getString(recverBankFullname));
            bank.setCardNumber(getString(recverBankAcctNo));
            // 商户退款
            bank.setCardType("7");
            repaymentParamBean.setBank(bank);
            logger.warn("商户的退款信息：{}",JSONObject.toJSONString(bank));
        }

        //获取差错订单号列表
        List<String> loanIdList = getMistakeLoanIdList(loanMainInfo.getCustomerId(), loanMainInfo.getTenantId());

        paymentService.refund(refundReq, repaymentParamBean, loanMainInfo, loanIdList);

        refundRes.setRefundAmount(refundReq.getRefundAmount());
        return refundRes;
    }

    /**
     * 转换成string
     * @param o
     * @return
     */
    public String getString(Object o) {
        return Objects.isNull(o) ? "" : o.toString();
    }

    /**
     * 提前还下期
     *
     * @param billPrepayNextReq
     * @return
     * @throws TxnBizException
     */
    @Override
    public BillPrepayNextRes billPrepayNext(BillPrepayNextReq billPrepayNextReq) throws TxnException {
        List<String> products = null;
        if (StringUtils.isNotBlank(billPrepayNextReq.getBizType())) {
            products = BizTypeProducts.getByBizType(BizTypeEnum.getByCode(billPrepayNextReq.getBizType()));
        }

        BillPrepayNextRes billPrepayNextRes = new BillPrepayNextRes();
        billPrepayNextRes.setUserId(billPrepayNextReq.getUserId());
        //校验请求报文以及获取机构 客户  银行 订单 等信息
        RepaymentParamBean repaymentParamBean = commonRepayment(billPrepayNextReq, billPrepayNextReq.getCardNumber());
        //客户号
        String customerId = repaymentParamBean.getCustomer().getSecondLevel().getCustomerId();
        //获取差错订单号列表
        List<String> loanIdList = getMistakeLoanIdList(customerId, billPrepayNextReq.getTenantId());

        //幂等性校验
        TlPaymentLog log = extTlPaymentLogMapper.selectByProductsAndPayId(billPrepayNextReq.getSeqNo(), customerId, billPrepayNextReq.getTenantId(), products);
        if (null != log) {
            List<TlPaymentMapping> tlPaymentMappingList = extTlPaymentMappingMapper.selectByPaymentSeqAndCustomerId(billPrepayNextReq.getSeqNo(), customerId, billPrepayNextReq.getTenantId());
            List<String> loanList = tlPaymentMappingList.stream().map(TlPaymentMapping::getLoanId).collect(Collectors.toList());
            billPrepayNextRes.setPaymentSeqNo(log.getSeqNo());
            billPrepayNextRes.setLoanList(loanList);
            return billPrepayNextRes;
        }

        //获取订单账户 增加小额现金贷的判断
        List<AmLoanAccountInfo> loanAccountInfos =
                extAmLoanAccountInfoMapper.selectByCustomerIdAndLoanTypeProductIdsAndNotStatus(billPrepayNextReq.getTenantId(),
                        customerId, billPrepayNextReq.getChannel(), Arrays.asList(TransBizConstant.ACCOUNT_STATUS_0,
                                TransBizConstant.ACCOUNT_STATUS_6, TransBizConstant.ACCOUNT_STATUS_7,
                                TransBizConstant.ACCOUNT_STATUS_8, TransBizConstant.ACCOUNT_STATUS_9),
                        Arrays.asList(TransBizConstant.LOAN_TYPE_2, TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_4, TransBizConstant.LOAN_TYPE_5, TransBizConstant.LOAN_TYPE_7), products);

        if (CollectionUtils.isEmpty(loanAccountInfos)) {
            logger.error(TxnRespCode.PAYMENT_LOAN_IS_EMPTY.getMsg());
            throw new TxnBizException(TxnRespCode.PAYMENT_LOAN_IS_EMPTY.getCode(), TxnRespCode.PAYMENT_LOAN_IS_EMPTY.getUserTips());
        }

        Date bussinessDate = repaymentParamBean.getOrg().getNextProcessingDate();

        for (AmLoanAccountInfo amLoanAccountInfo : loanAccountInfos) {

            AmLoanMainInfo loanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndCusId(amLoanAccountInfo.getLoanId(), customerId, amLoanAccountInfo.getTenantId());
            //还款校验
            validateBillPay(loanMainInfo, amLoanAccountInfo, loanIdList);
        }
        repaymentParamBean.setLoanAccountInfoList(loanAccountInfos);

        logger.debug("计算欠款并生成还款流水信息");
        BillResult billResult = paymentService.billPrepayNext(billPrepayNextReq, repaymentParamBean, bussinessDate);
        billPrepayNextRes.setPaymentSeqNo(billResult.getSeqNo());
        billPrepayNextRes.setLoanList(billResult.getLoanList());
        return billPrepayNextRes;
    }

    /**
     * 账单还款试算
     *
     * @param billPayTrailReq
     * @return
     * @throws TxnException
     */
    @Override
    public BillPayTrailRes billPayTrail(BillPayTrailReq billPayTrailReq) throws TxnException {
        logger.debug("1.验证及获取客户信息，用户号：{}", billPayTrailReq.getUserId());
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(billPayTrailReq.getUserId(), billPayTrailReq.getChannel(),
                billPayTrailReq.getTenantId(), null);
        // 客户日终账务处理
        custBatchService.dealCustBatch(customer.getCustomerId(), customer.getOrganizationId(), customer.getTenantId());

        String paymentMonth = billPayTrailReq.getPaymentMonth();
        String tenantId = billPayTrailReq.getTenantId();
        String customerId = customer.getCustomerId();
        String channel = billPayTrailReq.getChannel();

        List<String> products = null;
        if (StringUtils.isNotBlank(billPayTrailReq.getBizType())) {
            products = BizTypeProducts.getByBizType(BizTypeEnum.getByCode(billPayTrailReq.getBizType()));
        }

        //2 获取业务日期
        PrOrganizationTable organization =
                organizationTableService.findByOrId(customer.getOrganizationId(), tenantId);
        String orgId = organization.getOrganizationId();
        Date bussinessDate = organization.getNextProcessingDate();
        String productId = products.get(0);
        //2 获取查询月份账单日对应的还款日
        Integer paymentDd = customer.getPaymentDd();
        Integer statementDd = customer.getStatementDd();

        statementDd = this.getStatementDayByProductId(bussinessDate, statementDd, productId);
        Date queryStatementDate = DateUtils.getThisMonthDate(paymentMonth, DateUtils.YEAR_MONTH_PATTERN, statementDd);
        Date queryPaymentDate = loanHandlerImpl.getCurrentPaymentDate(queryStatementDate, paymentDd, bussinessDate, products);

        //获取当期及下期账单日
        CurrentStatementBean bean = loanHandlerImpl.getCurrentStatement(productId, statementDd, paymentDd, bussinessDate);
        Date statementDate = bean.getStatementDate();
        Date paymentDate = bean.getPaymentDate();
        //下一期账单日
        Date nextStatementDate = DateUtils.getDateByMonth(statementDate, 1);

        //如果大于下期账单日
        if (queryStatementDate.compareTo(nextStatementDate) > 0) {
            throw new TxnBizException(TxnRespCode.PAYMENT_MONTH_IS_ERROR.getCode(), TxnRespCode.PAYMENT_MONTH_IS_ERROR.getUserTips());
        }

        PrProductTable prProductTable = productTableServiceImpl.getPrProductTableByRule(TransBizConstant.LOAN_TYPE_3, tenantId, channel, orgId);
        BillPayTrailRes res = new BillPayTrailRes();
        res.setUserId(billPayTrailReq.getUserId());
        res.setAmount(BigDecimal.ZERO);
        res.setMinAmount(prProductTable.getMinRepayAmount());
        res.setCanDeferPayment(TransBizConstant.NO_FLAG);

        //获取订单账户 增加小额现金贷的判断
        List<AmLoanAccountInfo> loanAccountInfos =
                extAmLoanAccountInfoMapper.selectByCustomerIdAndLoanTypeProductIdsAndNotStatus(tenantId,
                        customerId, channel, Arrays.asList(TransBizConstant.ACCOUNT_STATUS_0,
                                TransBizConstant.ACCOUNT_STATUS_6, TransBizConstant.ACCOUNT_STATUS_7,
                                TransBizConstant.ACCOUNT_STATUS_8, TransBizConstant.ACCOUNT_STATUS_9),
                        Arrays.asList(TransBizConstant.LOAN_TYPE_2, TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_4, TransBizConstant.LOAN_TYPE_5, TransBizConstant.LOAN_TYPE_7), products);

        if (CollectionUtils.isEmpty(loanAccountInfos)) {
            return res;
        }

        List<AmLoanMainInfo> loanMainInfoList = extAmLoanMainInfoMapper.selectByCusIdtProductIdsAndTenantId(customer.getCustomerId(), customer.getTenantId(), products);

        //计算欠款
        BigDecimal amt = BigDecimal.ZERO;
        // 剩余未还本金
        BigDecimal principalAmnt= BigDecimal.ZERO;
        // 剩余未还利息
        BigDecimal interestAmnt= BigDecimal.ZERO;
        // 剩余未还罚息
        BigDecimal penaltyIntAmnt= BigDecimal.ZERO;
        List<String> loanIdList = loanAccountInfos.stream().map(AmLoanAccountInfo::getLoanId).collect(Collectors.toList());
        //获取所还款日未结清的还款计划
        List<AmRepaymentPlanInfo> planList =
                extAmRepaymentPlanInfoMapper.selectByLoanIdListAndStatus(loanIdList, customerId, tenantId,
                        Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2));
        if (CollectionUtils.isEmpty(planList)) {
            return res;
        }

        //账单月份为当期，且当月没有账单分期交易，当前未逾期，则可以
        if (statementDate.equals(queryStatementDate)
                && loanAccountInfos.stream().noneMatch(loan -> TransBizConstant.ACCOUNT_STATUS_2.equals(loan.getAccountStatus()))
                && loanMainInfoList.stream().filter(loan -> TransBizConstant.LOAN_TYPE_3.equals(loan.getLoanType()))
                .noneMatch(loan -> loan.getPostingDate().compareTo(statementDate) >= 0 && loan.getPostingDate().compareTo(paymentDate) <= 0)) {
            res.setCanDeferPayment(TransBizConstant.YES_FLAG);
        }

        //计算可分期金额
        BigDecimal stagingAmount = BigDecimal.ZERO;
        planList = planList.stream().filter(plan -> plan.getPaymentDueDate().compareTo(queryPaymentDate) <= 0).collect(Collectors.toList());
        for (AmRepaymentPlanInfo planInfo : planList) {
            DebtBean debtBean = debtCalculationService.getBalance(planInfo);
            amt = amt.add(debtBean.getTotalAmount());
            // 剩余未还本金
            principalAmnt = principalAmnt.add(debtBean.getPrincipalAmount());
            // 剩余未还利息
            interestAmnt= interestAmnt.add(debtBean.getInterestAmount());
            // 剩余未还罚息
            penaltyIntAmnt= penaltyIntAmnt.add(debtBean.getPenaltyAmount().add(debtBean.getCompountAmount()));
            AmLoanMainInfo amLoanMainInfo = loanMainInfoList.stream().filter(loan -> loan.getLoanId().equals(planInfo.getLoanId())).findFirst().get();
            if (TransBizConstant.LOAN_TYPE_4.equals(amLoanMainInfo.getLoanType()) &&
                    BizTypeEnum.getByCodeWhenNullException(billPayTrailReq.getBizType()).getCanStaging()) {
                stagingAmount = stagingAmount.add(debtBean.getTotalAmount());
            }
        }

        if (stagingAmount.compareTo(BigDecimal.ZERO) <= 0) {
            res.setCanDeferPayment(TransBizConstant.NO_FLAG);
        }
        res.setAmount(amt);
        res.setPrincipalAmnt(principalAmnt);// 剩余未还本金
        res.setInterestAmnt(interestAmnt);// 剩余未还利息
        res.setPenaltyIntAmnt(penaltyIntAmnt);// 剩余未还罚息
        return res;
    }

    /**
     * 根据产品获取还款日
     * @param loanDate 贷款日期
     * @param paymentDd 默认的还款日
     * @param productId 产品编号
     * @return 还款日
     */
    @Override
    public int getPaymentDayByProductId(Date loanDate, int paymentDd, String productId) {
        Integer paymentDay = paymentDd;
        try {
            paymentDay = loanService.getPaymentDayByRule(Constants.DEFAULT_ORG_NUMBER, productId, loanDate, Constants.DEFAULT_TENANT_ID);
        } catch (Exception e) {
            logger.warn("产品productId:{}没有对应的还款日规则配置，默认的还款日是：{}", productId, paymentDay);
        }
        return paymentDay;
    }

    /**
     * 根据产品获取账单日
     * @param loanDate 贷款日期
     * @param statementDd 默认的账单日
     * @param productId 产品编号
     * @return 账单日
     */
    @Override
    public int getStatementDayByProductId(Date loanDate, int statementDd, String productId) {
        Integer statementDdDay = statementDd;
        try {
            statementDdDay =  loanService.getStatementDayByRule(Constants.DEFAULT_ORG_NUMBER, productId, loanDate, Constants.DEFAULT_TENANT_ID);;
        } catch (Exception e) {
            e.printStackTrace();
            logger.warn("产品productId:{}没有对应的账单日规则配置",productId);
        }
        return statementDdDay;
    }


    @Override
    public CustRepaymentQueryRes custRepaymentQuery(CustRepaymentQueryReq req) throws TxnException {
        CustRepaymentQueryRes res = new CustRepaymentQueryRes();
        res.setSeqNo(req.getSeqNo());
        res.setUserId(req.getUserId());
        List<String> products = null;
        if (StringUtils.isNotBlank(req.getBizType())) {
            products = BizTypeProducts.getByBizType(BizTypeEnum.getByCode(req.getBizType()));
        }

        //获取客户信息
        CmCustomerSecondLevel customer = customerService.getSecondLevelByCondition(req.getUserId(), req.getChannel(),
                req.getTenantId(), null);

        Integer pageNum = Integer.parseInt(req.getPageNum());
        Integer pageSize = Integer.parseInt(req.getPageSize());
        Page<TlPaymentLog> page = PageHelper.startPage(pageNum, pageSize);
        String sumStatus = null;
        if (!BizTypeEnum.BIZ_TYPE_XAJ.getCode().equalsIgnoreCase(req.getBizType())){
            // 非兴安借产品不查询失败记录
            sumStatus = TransBizConstant.PAYMENT_LOG_SUM_STATUS_F;
        }
        List<TlPaymentLog> paymentLogList = extTlPaymentLogMapper.selectByCustTenantIdProductIdsAndNotSumStatusDesc(customer.getCustomerId(), customer.getTenantId(), sumStatus, products);

        //总页数
        res.setTotalPage(page.getPages() + "");

        if (CollectionUtils.isEmpty(paymentLogList)) {
            return res;
        }

        //组装数据
        List<Repayment> list = new ArrayList<>();
        for (TlPaymentLog log : paymentLogList) {
            Repayment repayment = new Repayment();
            repayment.setPaymentSeqNo(log.getSeqNo());
            repayment.setPaymentCardNo(log.getPayerAccount());
            String payType = "";
            switch (log.getPaymentType()) {
                case TransBizConstant.REPAYMENT_TYPE_AUTO:
                    payType = "02";
                    break;
                case TransBizConstant.REPAYMENT_TYPE_REFUND:
                    payType = "03";
                    break;
                default:
                    payType = "01";
                    break;
            }
            repayment.setPaymentType(payType);
            if (TransBizConstant.PAYMENT_LOG_SUM_STATUS_S.equals(log.getSumStatus())) {
                repayment.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_S);
            } else if (TransBizConstant.PAYMENT_LOG_SUM_STATUS_F.equals(log.getSumStatus())){
                repayment.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_F);
            }  else {
                repayment.setSumStatus("P");
            }
            repayment.setRepayAmount(log.getAmount());
            repayment.setRepayDate(log.getBusinessDate());
            repayment.setLaunchTime(DateUtils.format(log.getLaunchTime(), DateUtils.DATETIME_PATTERN));

            if (log.getPaymentType().equals(TransBizConstant.REPAYMENT_TYPE_REFUND)) {
                //查询还款映射
                List<TlPaymentMapping> mappingList = extTlPaymentMappingMapper.selectByPaymentSeqAndCustomerId(log.getSeqNo(), log.getCustomerId(), log.getTenantId());
                if (CollectionUtils.isNotEmpty(mappingList)) {
                    //查询订单信息
                    AmLoanMainInfo main = extAmLoanMainInfoMapper.selectByLoanIdAndCusId(mappingList.get(0).getLoanId(), log.getCustomerId(), log.getTenantId());
                    repayment.setLoanId(main.getLoanId());
                    repayment.setBusinessDate(main.getPostingDate());
                    repayment.setLoanAmount(main.getLoanAmount());
                    repayment.setTxnTime(DateUtils.format(main.getTxnTime(), DateUtils.DATETIME_PATTERN));
                    /*// 23.30 切日后，前端显示实际发生的时间
                    if (!DateUtils.sameDate(DateUtils.parseDate(main.getTxnTime()).getTime(), DateUtils.parseDate(main.getCreateTime()).getTime())) {
                        // 交易时间
                        repayment.setTxnTime(DateUtils.format(DateUtils.addDay(main.getTxnTime(), -1), DateUtils.DATETIME_PATTERN));
                    }
                    if (!Objects.isNull(log.getBusinessDate()) && !DateUtils.sameDate(DateUtils.parseDate(log.getBusinessDate()).getTime(), DateUtils.parseDate(log.getCreateTime()).getTime())) {
                        // 还款日期
                        repayment.setRepayDate(DateUtils.addDay(log.getBusinessDate(), -1));
                        // 业务日期
                        repayment.setBusinessDate(DateUtils.addDay(log.getBusinessDate(), -1));
                    }*/

                    repayment.setDescription(main.getMemoid());
                    repayment.setPaymentMode("兴安花");
                }
            }

            list.add(repayment);
        }
        res.setRepaymentList(list);
        return res;
    }

    /**
     * 借据结清信息查询--默认只提供给信用就医
     *
     * @param loanPayOffReq
     * @return
     */
    @Override
    public CustPayOffQueryRes loanInfoQuery(LoanPayOffReq loanPayOffReq) throws TxnException {
        logger.info("借据结清信息查询。loanPayOffReq：{}", loanPayOffReq);
        CustomerRes customer = customerService.getCustomerByChannelAndUserId(loanPayOffReq.getChannelNo(),
                loanPayOffReq.getUserId(), loanPayOffReq.getTenantId());

        String customerId = customer.getFirstLevel().getCustomerId();
        if (StringUtils.isEmpty(customerId)) {
            logger.info("客户信息不存在,userId:{}",loanPayOffReq.getUserId());
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());

        }
        //查询还款分配记录
        List<TlPaymentAllocationLog> allLogs = extTlPaymentAllocationLogMapper.selectBySeqNoAndTenantId(loanPayOffReq.getSeqNo(), loanPayOffReq.getTenantId());
        if(CollectionUtils.isEmpty(allLogs)){
            logger.info("还款分配记录不存在，seqNo：{}",loanPayOffReq.getSeqNo());
        }
        //根据渠道客户号查询订单列表,账户状态（0-新建 1-正常 2-逾期 7-提前结清 8-正常结清 9-取消）
        List<LoanRecordRes> loanRecordResList = loanService.getLoanMainByCondition(loanPayOffReq.getChannelNo(), customerId, loanPayOffReq.getTenantId());
        List<String> settleStatusList = Arrays.asList(TransBizConstant.ACCOUNT_STATUS_7, TransBizConstant.ACCOUNT_STATUS_8);
        //此客户下 结清的记录
        List<LoanRecordRes> settleList = loanRecordResList.stream().filter(loanRecordRes ->
                settleStatusList.contains(loanRecordRes.getAccountStatus())).collect(Collectors.toList());
        List<LoanPayOffDetailRes> loanPayOffDetailList = new ArrayList<>();
        CustPayOffQueryRes custPayOffQueryRes = new CustPayOffQueryRes();
        //查询订单信息
        for (TlPaymentAllocationLog paymentAllocationLog : allLogs) {
            //根据渠道客户号查询订单列表,账户状态
            for (LoanRecordRes loanRecordRes : settleList) {
                if (loanRecordRes.getLoanId().equals(paymentAllocationLog.getLoanId())) {
                    List<AmRepaymentPlanInfo> planInfoList = extAmRepaymentPlanInfoMapper.selectByLoanIdAndStatus(paymentAllocationLog.getLoanId()
                            , Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_7, TransBizConstant.LOAN_PLAN_STATUS_8)
                            , customerId, loanPayOffReq.getTenantId());
                    if (CollectionUtils.isEmpty(planInfoList)) {
                        logger.error("未查询到订单号：{}对应的还款计划", paymentAllocationLog.getLoanId());
                        throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
                    }
                    LoanPayOffDetailRes loanPayOffDetailRes = new LoanPayOffDetailRes();
                    loanPayOffDetailRes.setLoanId(paymentAllocationLog.getLoanId());
                    //还款金额
                    loanPayOffDetailRes.setTxnAmt(paymentAllocationLog.getPostingAmount());
                    //应还款日期  信用就医无分期，每条借据对应一个还款计划
                    loanPayOffDetailRes.setPayDate(planInfoList.get(0).getPaymentDueDate());
                    //实际还款日期
                    loanPayOffDetailRes.setActualPayDate(planInfoList.get(0).getTransactionDate());
                    //逾期天数
                    loanPayOffDetailRes.setDelqDays(planInfoList.get(0).getDelqDays());
                    loanPayOffDetailRes.setCurrency(TransBizConstant.CURRENCY_CNY);
                    loanPayOffDetailList.add(loanPayOffDetailRes);

                }

            }
        }
        BeanUtils.copyProperties(loanPayOffReq,custPayOffQueryRes);
        custPayOffQueryRes.setLoanPayOffDetailList(loanPayOffDetailList);

        return custPayOffQueryRes;
    }

    @Override
    public EtlSettleRes getEtlSettleDate(EtlSettleReq etlSettleReq) throws TxnException {
        EtlSettleRes etlSettleRes = new EtlSettleRes();
        List<TlPaymentLog> tlPaymentLogList = extTlPaymentLogMapper.selectByCustomerIdProductsAndSumStatus(etlSettleReq.getCustomerId(), new String[]{"S"}, etlSettleReq.getTenantId(), etlSettleReq.getProducts());
        if (null != tlPaymentLogList && !tlPaymentLogList.isEmpty()) {
            Date etlSettleDate = tlPaymentLogList.stream().max(Comparator.comparing(TlPaymentLog::getBusinessDate)).get().getBusinessDate();
            etlSettleRes.setEtlSettleDate(etlSettleDate);
        }
        return etlSettleRes;
    }

    /**
     * 客户借据还款记录查询
     *
     * @param repaymentQueryReq
     * @return
     */
    @Override
    public LoanRepaymentQueryRes loanRepaymentQuery(LoanRepaymentQueryReq repaymentQueryReq) throws TxnBizException {
        String tenantId = repaymentQueryReq.getTenantId();
        LoanRepaymentQueryRes res = new LoanRepaymentQueryRes();
        CmCustomerSecondLevel customerRes = customerService.getSecondLevelByCondition(repaymentQueryReq.getUserId(), repaymentQueryReq.getChannel(),
                tenantId, null);
        //查询还款分配流水表
        List<TlPaymentAllocationLog> allocationLogList = extTlPaymentAllocationLogMapper.selectListByLoanId(tenantId, customerRes.getCustomerId(), repaymentQueryReq.getLoanId(),
                TransBizConstant.PAYMENT_ALLOCATION_TYPE_09,
                Arrays.asList(TransBizConstant.TXN_CODE_INST_PYMT, TransBizConstant.TXN_CODE_INST_PYMT_INTR, TransBizConstant.TXN_CODE_INST_PYMT_PEN_INTR, TransBizConstant.TXN_CODE_INST_PYMT_PEN_SEC,
                        TransBizConstant.TXN_CODE_INST_PYMT_INTR_SEC));
        List<LoanRepaymentVo> loanRepayments = new ArrayList<>();
        Map<String, List<TlPaymentAllocationLog>> allocationLogMap = allocationLogList.stream().collect(Collectors.groupingBy(TlPaymentAllocationLog::getSeqNo));
        allocationLogMap.forEach(
                (seqNo, list) -> {
                    LoanRepaymentVo vo = new LoanRepaymentVo();
                    vo.setPaySeq(seqNo);
                    // 已还本金
                    BigDecimal paidPrincipalAmount = list.stream()
                            .filter(log -> TransBizConstant.TXN_CODE_INST_PYMT.equalsIgnoreCase(log.getTxnCode()))
                            .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                    vo.setPaidPrincipalAmount(paidPrincipalAmount);
                    // 已还利息
                    BigDecimal paidInterestAmount = list.stream()
                            .filter(log -> Arrays.asList(TransBizConstant.TXN_CODE_INST_PYMT_INTR,TransBizConstant.TXN_CODE_INST_PYMT_INTR_SEC).contains(log.getTxnCode()))
                            .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                    vo.setPaidInterestAmount(paidInterestAmount);
                    // 已还罚息
                    BigDecimal penaltyAmount = list.stream().filter(log -> TransBizConstant.TXN_CODE_INST_PYMT_PEN_SEC.equalsIgnoreCase(log.getTxnCode()))
                            .map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                    vo.setPaidPenaltyAmount(penaltyAmount);
                    vo.setBusinessTime(list.get(0).getCreateTime());
                    vo.setPaidAllocationAmount(paidPrincipalAmount.add(paidInterestAmount).add(penaltyAmount));
                    loanRepayments.add(vo);
                });
        // 已还本金汇总
        BigDecimal paidPrincipalAmountSum = loanRepayments.stream().map(LoanRepaymentVo::getPaidPrincipalAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        res.setPaidPrincipalAmountSum(paidPrincipalAmountSum);
        // 已还利息汇总
        BigDecimal paidInterestAmountSum = loanRepayments.stream().map(LoanRepaymentVo::getPaidInterestAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        res.setPaidInterestAmountSum(paidInterestAmountSum);
        // 已还罚息费汇总
        BigDecimal penaltyAmountSum = loanRepayments.stream().map(LoanRepaymentVo::getPaidPenaltyAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        res.setPaidPenaltyAmountSum(penaltyAmountSum);
        List<LoanRepaymentVo> sortLists = loanRepayments.stream().sorted(Comparator.comparing(LoanRepaymentVo::getBusinessTime).reversed()).collect(Collectors.toList());
        res.setLoanRepayments(sortLists);
        return res;
    }


}
